Compare commits

4 Commits

Author SHA1 Message Date
d4413c433f fmt 2025-11-02 11:04:12 +02:00
d9a4bb7871 fix fmt 2025-11-02 10:17:49 +02:00
8e017af3ed change sv1 version 2025-11-01 10:22:14 +02:00
ef6023330d 2025-10-22 15:05:12 +03:00
12 changed files with 199 additions and 93 deletions

View File

@@ -55,30 +55,22 @@ pure-run:
exec ./$(BIN_DIR)/$(APP_NAME)
test:
@go test ./... | grep -v '^?' || true
@cd src && go test ./... | grep -v '^?' || true
fmt:
@go fmt ./internal/./...
@go fmt ./cmd/./...
@go fmt ./hooks/./...
@$(GOPATH)/bin/goimports -w ./internal/
@$(GOPATH)/bin/goimports -w ./cmd/
@$(GOPATH)/bin/goimports -w ./hooks/
@cd src && go fmt .
@cd src && $(GOPATH)/bin/goimports -w .
vet:
@go vet ./...
lint:
@$(GOPATH)/bin/golangci-lint run
@cd src && go vet ./...
check: fmt vet lint test
licenses:
lint:
@cd src && $(GOPATH)/bin/golangci-lint run ./...
@$(GOPATH)/bin/go-licenses save ./... --save_path=third_party/licenses --force
@echo "Licenses have been exported to third_party/licenses"
clean:
@rm -rf bin
licenses:
@cd src && $(GOPATH)/bin/go-licenses save ./... --save_path=../third_party/licenses --force
@echo "Licenses have been exported to third_party/licenses"
help:
@echo "Available commands: $$(cat Makefile | grep -E '^[a-zA-Z_-]+:.*?' | grep -v -- '-setup:' | sed 's/:.*//g' | sort | uniq | tr '\n' ' ')"

22
com/Access/_common.lua Normal file
View File

@@ -0,0 +1,22 @@
-- File com/Access/_common.lua
--
-- Created at 2025-21-10
--
-- Description:
--- Common functions for Unit module
local common = {}
function common.CheckMissingElement(arr, cmp)
local is_missing = {}
local ok = true
for _, key in ipairs(arr) do
if cmp[key] == nil then
table.insert(is_missing, key)
ok = false
end
end
return ok, is_missing
end
return common

30
com/Access/_errors.lua Normal file
View File

@@ -0,0 +1,30 @@
-- File com/Access/_errors.lua
--
-- Created at 2025-21-10
-- Description:
--- Centralized error definitions for Access operations
--- to keep API responses consistent and clean.
local errors = {
-- Common validation
MISSING_PARAMS = { code = -32602, message = "Missing params" },
INVALID_FIELD_TYPE = { code = -32602, message = "'fields' must be a non-empty table" },
INVALID_BY_PARAM = { code = -32602, message = "Invalid 'by' param" },
NO_VALID_FIELDS = { code = -32604, message = "No valid fields to update" },
-- Existence / duplication
UNIT_NOT_FOUND = { code = -32102, message = "Unit is not exists" },
UNIT_EXISTS = { code = -32101, message = "Unit is already exists" },
-- Database & constraint
UNIQUE_CONSTRAINT = { code = -32602, message = "Unique constraint failed" },
DB_QUERY_FAILED = { code = -32001, message = "Database query failed" },
DB_EXEC_FAILED = { code = -32002, message = "Database execution failed" },
DB_INSERT_FAILED = { code = -32003, message = "Failed to create unit" },
DB_DELETE_FAILED = { code = -32004, message = "Failed to delete unit" },
-- Generic fallback
UNKNOWN = { code = -32099, message = "Unexpected internal error" },
}
return errors

View File

@@ -1,4 +1,4 @@
// The cmd package is the main package where all the main hooks and methods are called.
// The cmd package is the main package where all the main hooks and methods are called.
// GoSally uses spf13/cobra to organize all the calls.
package cmd
@@ -24,7 +24,7 @@ scripts in a given directory. For more information, visit: https://gosally.oblat
},
}
// Execute prepares global log, loads cmdline args
// Execute prepares global log, loads cmdline args
// and executes rootCmd.Execute()
func Execute() {
log.SetOutput(os.Stdout)

View File

@@ -85,7 +85,7 @@ func InitUUIDHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
corestate.NODE_UUID = uuid32
}
// The hook is responsible for checking the initialization stage
// The hook is responsible for checking the initialization stage
// and restarting in some cases
func InitRuntimeHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
if *x.Config.Env.ParentStagePID != os.Getpid() {
@@ -138,7 +138,7 @@ func InitRuntimeHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
}
// post-init stage
// The hook creates a run.lock file, which contains information
// The hook creates a run.lock file, which contains information
// about the process and the node, in the runtime directory.
func InitRunlockHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
NodeApp.Fallback(func(ctx context.Context, cs *corestate.CoreState, x *app.AppX) {
@@ -192,7 +192,7 @@ func InitRunlockHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
}
}
// The hook reads the configuration and replaces special expressions
// The hook reads the configuration and replaces special expressions
// (%tmp% and so on) in string fields with the required data.
func InitConfigReplHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
if !slices.Contains(*x.Config.Conf.DisableWarnings, "--WNonStdTmpDir") && os.TempDir() != "/tmp" {
@@ -218,7 +218,7 @@ func InitConfigReplHook(_ context.Context, cs *corestate.CoreState, x *app.AppX)
}
}
// The hook is responsible for outputting the
// The hook is responsible for outputting the
// final config and asking for confirmation.
func InitConfigPrintHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) {
if *x.Config.Conf.Node.ShowConfig {
@@ -254,7 +254,7 @@ func InitSLogHook(_ context.Context, cs *corestate.CoreState, x *app.AppX) {
*x.SLog = *newSlog
}
// The method goes through the entire config structure through
// The method goes through the entire config structure through
// reflection and replaces string fields with the required ones.
func processConfig(conf any, replacements map[string]any) error {
val := reflect.ValueOf(conf)

View File

@@ -11,8 +11,8 @@ import (
"regexp"
"time"
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
"github.com/akyaiy/GoSally-mvp/src/internal/colors"
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
"github.com/akyaiy/GoSally-mvp/src/internal/core/run_manager"
"github.com/akyaiy/GoSally-mvp/src/internal/core/update"
"github.com/akyaiy/GoSally-mvp/src/internal/core/utils"
@@ -22,6 +22,7 @@ import (
"github.com/akyaiy/GoSally-mvp/src/internal/server/gateway"
"github.com/akyaiy/GoSally-mvp/src/internal/server/session"
"github.com/akyaiy/GoSally-mvp/src/internal/server/sv1"
"github.com/akyaiy/GoSally-mvp/src/internal/server/sv2"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/spf13/cobra"
@@ -65,13 +66,20 @@ func RunHook(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error {
Ver: "v1",
})
sv2 := sv2.InitServer(&sv2.HandlerInitStruct{
X: x,
CS: cs,
AllowedCmd: regexp.MustCompile(AllowedCmdPattern),
Ver: "v2",
})
session_manager := session.New(*x.Config.Conf.HTTPServer.SessionTTL)
s := gateway.InitGateway(&gateway.GatewayServerInit{
SM: session_manager,
CS: cs,
X: x,
}, serverv1)
}, serverv1, sv2)
r := chi.NewRouter()
r.Use(cors.Handler(cors.Options{

View File

@@ -15,7 +15,7 @@ func NewLuaPool() *LuaPool {
pool: sync.Pool{
New: func() any {
L := lua.NewState()
return L
},
},
@@ -30,6 +30,6 @@ func (lp *LuaPool) Put(L *lua.LState) {
L.Close()
newL := lua.NewState()
lp.pool.Put(newL)
}

View File

@@ -11,13 +11,12 @@ import (
type LuaEngineDeps struct {
HttpRequest *http.Request
JSONRPCRequest *rpc.RPCRequest
SessionUUID string
ScriptPath string
SessionUUID string
ScriptPath string
}
type LuaEngineContract interface {
Handle(deps *LuaEngineDeps) *rpc.RPCResponse
}
type LuaEngine struct {

View File

@@ -80,10 +80,10 @@ func loadDBMod(llog *slog.Logger, sid string) func(*lua.LState) int {
mt := L.NewTypeMetatable("gosally_db")
L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
"exec": dbExec,
"query": dbQuery,
"exec": dbExec,
"query": dbQuery,
"query_row": dbQueryRow,
"close": dbClose,
"close": dbClose,
}))
L.SetField(dbMod, "__seed", lua.LString(sid))
@@ -215,43 +215,43 @@ func dbExec(L *lua.LState) int {
}
func dbQueryRow(L *lua.LState) int {
ud := L.CheckUserData(1)
conn, ok := ud.Value.(*DBConnection)
if !ok {
L.Push(lua.LNil)
L.Push(lua.LString("invalid database connection"))
return 2
}
ud := L.CheckUserData(1)
conn, ok := ud.Value.(*DBConnection)
if !ok {
L.Push(lua.LNil)
L.Push(lua.LString("invalid database connection"))
return 2
}
query := L.CheckString(2)
query := L.CheckString(2)
var args []any
if L.GetTop() >= 3 {
params := L.CheckTable(3)
params.ForEach(func(k lua.LValue, v lua.LValue) {
args = append(args, ConvertLuaTypesToGolang(v))
})
}
var args []any
if L.GetTop() >= 3 {
params := L.CheckTable(3)
params.ForEach(func(k lua.LValue, v lua.LValue) {
args = append(args, ConvertLuaTypesToGolang(v))
})
}
if conn.log {
conn.logger.Info("DB QueryRow",
slog.String("query", query),
slog.Any("params", args))
}
if conn.log {
conn.logger.Info("DB QueryRow",
slog.String("query", query),
slog.Any("params", args))
}
mtx := getDBMutex(conn.dbPath)
mtx.RLock()
defer mtx.RUnlock()
mtx := getDBMutex(conn.dbPath)
mtx.RLock()
defer mtx.RUnlock()
db, err := sql.Open("sqlite", conn.dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_sync=NORMAL&_cache_size=-10000")
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
defer db.Close()
db, err := sql.Open("sqlite", conn.dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_sync=NORMAL&_cache_size=-10000")
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))
return 2
}
defer db.Close()
row := db.QueryRow(query, args...)
row := db.QueryRow(query, args...)
columns := []string{}
stmt, err := db.Prepare(query)
@@ -278,36 +278,36 @@ func dbQueryRow(L *lua.LState) int {
columns = append(columns, c)
}
colCount := len(columns)
values := make([]any, colCount)
valuePtrs := make([]any, colCount)
for i := range columns {
valuePtrs[i] = &values[i]
}
colCount := len(columns)
values := make([]any, colCount)
valuePtrs := make([]any, colCount)
for i := range columns {
valuePtrs[i] = &values[i]
}
err = row.Scan(valuePtrs...)
if err != nil {
if err == sql.ErrNoRows {
L.Push(lua.LNil)
return 1
}
L.Push(lua.LNil)
L.Push(lua.LString(fmt.Sprintf("scan failed: %v", err)))
return 2
}
err = row.Scan(valuePtrs...)
if err != nil {
if err == sql.ErrNoRows {
L.Push(lua.LNil)
return 1
}
L.Push(lua.LNil)
L.Push(lua.LString(fmt.Sprintf("scan failed: %v", err)))
return 2
}
rowTable := L.NewTable()
for i, col := range columns {
val := values[i]
if val == nil {
L.SetField(rowTable, col, lua.LNil)
} else {
L.SetField(rowTable, col, ConvertGolangTypesToLua(L, val))
}
}
rowTable := L.NewTable()
for i, col := range columns {
val := values[i]
if val == nil {
L.SetField(rowTable, col, lua.LNil)
} else {
L.SetField(rowTable, col, ConvertGolangTypesToLua(L, val))
}
}
L.Push(rowTable)
return 1
L.Push(rowTable)
return 1
}
func dbQuery(L *lua.LState) int {

View File

@@ -9,7 +9,7 @@ import (
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
)
var SV1Version = "null"
var SV1Version = "v1"
// HandlerV1InitStruct structure is only for initialization
type HandlerV1InitStruct struct {

View File

@@ -0,0 +1,12 @@
package sv2
import (
"context"
"net/http"
"github.com/akyaiy/GoSally-mvp/src/internal/server/rpc"
)
func (h *Handler) Handle(_ context.Context, sid string, r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse {
return nil
}

View File

@@ -0,0 +1,43 @@
// SV2 works with binaries, scripts, and anything else that has access to stdin/stdout.
// Modules run in a separate process and communicate via I/O.
package sv2
import (
"regexp"
"github.com/akyaiy/GoSally-mvp/src/internal/core/corestate"
"github.com/akyaiy/GoSally-mvp/src/internal/engine/app"
)
// HandlerV2InitStruct structure is only for initialization
type HandlerInitStruct struct {
Ver string
CS *corestate.CoreState
X *app.AppX
AllowedCmd *regexp.Regexp
}
type Handler struct {
cs *corestate.CoreState
x *app.AppX
// allowedCmd and listAllowedCmd are regular expressions used to validate command names.
allowedCmd *regexp.Regexp
ver string
}
func InitServer(o *HandlerInitStruct) *Handler {
return &Handler{
cs: o.CS,
x: o.X,
allowedCmd: o.AllowedCmd,
ver: o.Ver,
}
}
// GetVersion returns the API version of the HandlerV1, which is set during initialization.
// This version is used to identify the API version in the request routing.
func (h *Handler) GetVersion() string {
return h.ver
}