diff --git a/Makefile b/Makefile index 815a0c5..dd66151 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ APP_NAME := node BIN_DIR := bin GOPATH := $(shell go env GOPATH) +export CONFIG_PATH := ./config.yaml +CGO_CFLAGS := -I/usr/local/include +CGO_LDFLAGS := -L/usr/local/lib -llua5.1 -lm -ldl .PHONY: all build run runq test fmt vet lint check clean all: build @@ -20,12 +23,17 @@ setup: lint-setup goimports-setup golicenses-setup @echo "Setup complete. Run 'make build' to compile the application." build: - @go build -o $(BIN_DIR)/$(APP_NAME) ./cmd/$(APP_NAME) + @echo "Building..." + @echo "CGO_CFLAGS is: '$(CGO_CFLAGS)'" + @echo "CGO_LDFLAGS is: '$(CGO_LDFLAGS)'" + CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build -o $(BIN_DIR)/$(APP_NAME) ./cmd/$(APP_NAME) run: build + @echo "Running!" ./$(BIN_DIR)/$(APP_NAME) runq: build + @echo "Running!" ./$(BIN_DIR)/$(APP_NAME) | jq test: diff --git a/cmd/node/node.go b/cmd/node/node.go index 99b9337..239ae1d 100644 --- a/cmd/node/node.go +++ b/cmd/node/node.go @@ -5,10 +5,10 @@ import ( "net/http" "regexp" - "github.com/akyaiy/GoSally-mvp/config" - gs "github.com/akyaiy/GoSally-mvp/general_server" - "github.com/akyaiy/GoSally-mvp/logs" - "github.com/akyaiy/GoSally-mvp/sv1" + "github.com/akyaiy/GoSally-mvp/core/config" + gs "github.com/akyaiy/GoSally-mvp/core/general_server" + "github.com/akyaiy/GoSally-mvp/core/logs" + "github.com/akyaiy/GoSally-mvp/core/sv1" "github.com/go-chi/chi/v5" ) @@ -38,6 +38,7 @@ func main() { Log: *logs.SetupLogger(cfg.Mode), Config: cfg, }, serverv1) + r := chi.NewRouter() r.Route("/api/{ver}/com", func(r chi.Router) { r.Get("/", s.HandleList) diff --git a/com/_prepare.lua b/com/_prepare.lua index 63b109a..f083f30 100644 --- a/com/_prepare.lua +++ b/com/_prepare.lua @@ -1,4 +1,5 @@ -package.path = "./com/?.lua;" .. package.path +package.path = package.path .. ";/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua" .. ";./com/?.lua;" +package.cpath = package.cpath .. ";/usr/lib64/lua/5.1/?.so;/usr/local/lib/lua/5.1/?.so" print = function() end io.write = function(...) end diff --git a/com/http.lua b/com/http.lua new file mode 100644 index 0000000..9054695 --- /dev/null +++ b/com/http.lua @@ -0,0 +1,15 @@ +package.path = package.path .. ";/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua;" .. ";./com/?.lua;" +package.cpath = package.cpath .. ";/usr/lib64/lua/5.1/?.so;/usr/local/lib/lua/5.1/?.so;" + +local https = require("ssl.https") +local ltn12 = require("ltn12") + +local response = {} +local res, code, headers = https.request{ + url = "https://localhost:8080/api/v1/echo?msg=sigma", + sink = ltn12.sink.table(response) +} + + +Result.msg = table.concat(response) +Result.status = "ok" diff --git a/cfg/config.yaml b/config.yaml similarity index 100% rename from cfg/config.yaml rename to config.yaml diff --git a/config/config.go b/core/config/config.go similarity index 100% rename from config/config.go rename to core/config/config.go diff --git a/general_server/hanle_multi.go b/core/general_server/hanle_multi.go similarity index 94% rename from general_server/hanle_multi.go rename to core/general_server/hanle_multi.go index 0161b73..288833c 100644 --- a/general_server/hanle_multi.go +++ b/core/general_server/hanle_multi.go @@ -7,7 +7,7 @@ import ( "net/http" "slices" - "github.com/akyaiy/GoSally-mvp/config" + "github.com/akyaiy/GoSally-mvp/core/config" "github.com/go-chi/chi/v5" ) @@ -81,7 +81,7 @@ func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) { ), ) - s.log.Info("Received request") + s.log.Debug("Received request") if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok { srv.Handle(w, r) @@ -90,7 +90,7 @@ func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) { if slices.Contains(s.cfg.Layers, serverReqApiVer) { if srv, ok := s.servers[serversApiVer(s.cfg.LatestVer)]; ok { - s.log.Info("Using latest version under custom layer", + s.log.Debug("Using latest version under custom layer", slog.String("layer", serverReqApiVer), slog.String("fallback-version", s.cfg.LatestVer), ) @@ -120,7 +120,7 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { ), ) - log.Info("Received request") + log.Debug("Received request") if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok { srv.HandleList(w, r) @@ -129,7 +129,7 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { if slices.Contains(s.cfg.Layers, serverReqApiVer) { if srv, ok := s.servers[serversApiVer(s.cfg.LatestVer)]; ok { - log.Info("Using latest version under custom layer", + log.Debug("Using latest version under custom layer", slog.String("layer", serverReqApiVer), slog.String("fallback-version", s.cfg.LatestVer), ) diff --git a/logs/logger.go b/core/logs/logger.go similarity index 100% rename from logs/logger.go rename to core/logs/logger.go diff --git a/core/sv1/handle_com.go b/core/sv1/handle_com.go new file mode 100644 index 0000000..e8a9bd6 --- /dev/null +++ b/core/sv1/handle_com.go @@ -0,0 +1,174 @@ +package sv1 + +import ( + "encoding/json" + "log/slog" + "net/http" + "os" + "path/filepath" + + lua "github.com/aarzilli/golua/lua" + + "github.com/go-chi/chi/v5" +) + +func (h *HandlerV1) _handle() { + uuid16 := h.newUUID() + log := h.log.With( + slog.Group("request", + slog.String("version", h.GetVersion()), + slog.String("url", h.r.URL.String()), + slog.String("method", h.r.Method), + ), + slog.Group("connection", + slog.String("connection-uuid", uuid16), + slog.String("remote", h.r.RemoteAddr), + ), + ) + log.Info("Received request") + + cmd := chi.URLParam(h.r, "cmd") + var scriptPath string + if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) { + log.Error("HTTP request error", + slog.String("error", "invalid command"), + slog.String("cmd", cmd), + slog.Int("status", http.StatusBadRequest)) + h.writeJSONError(http.StatusBadRequest, "invalid command") + return + } + if !h.listAllowedCmd.MatchString(cmd) { + log.Error("HTTP request error", + slog.String("error", "invalid command"), + slog.String("cmd", cmd), + slog.Int("status", http.StatusBadRequest)) + h.writeJSONError(http.StatusBadRequest, "invalid command") + return + } + if scriptPath = h.comMatch(chi.URLParam(h.r, "ver"), cmd); scriptPath == "" { + log.Error("HTTP request error", + slog.String("error", "command not found"), + slog.String("cmd", cmd), + slog.Int("status", http.StatusNotFound)) + h.writeJSONError(http.StatusNotFound, "command not found") + return + } + + scriptPath = filepath.Join(h.cfg.ComDir, scriptPath) + if _, err := os.Stat(scriptPath); err != nil { + log.Error("HTTP request error", + slog.String("error", "command not found"), + slog.String("cmd", cmd), + slog.Int("status", http.StatusNotFound)) + h.writeJSONError(http.StatusNotFound, "command not found") + return + } + + L := lua.NewState() + defer L.Close() + L.OpenLibs() + + // Создаем таблицу Params + L.NewTable() + paramsTableIndex := L.GetTop() // Индекс таблицы в стеке + + // Заполняем таблицу из query параметров + qt := h.r.URL.Query() + for k, v := range qt { + if len(v) > 0 { + L.PushString(v[0]) // Значение + L.SetField(paramsTableIndex, k) // paramsTable[k] = v[0] + } + } + + // Помещаем Params в глобальные переменные + L.SetGlobal("Params") + + // Создаем пустую таблицу Result + L.NewTable() + L.SetGlobal("Result") + + // Загружаем и выполняем скрипт подготовки окружения, если есть + prepareLuaEnv := filepath.Join(h.cfg.ComDir, "_prepare.lua") + if _, err := os.Stat(prepareLuaEnv); err == nil { + if err := L.DoFile(prepareLuaEnv); err != nil { + log.Error("Failed to prepare lua environment", + slog.String("error", err.Error())) + h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) + return + } + } else { + log.Error("No environment preparation script found, skipping preparation", + slog.String("error", err.Error())) + } + + // Выполняем основной Lua скрипт + if err := L.DoFile(scriptPath); err != nil { + log.Error("Failed to execute lua script", + slog.Group("lua-status", + slog.String("error", err.Error()), + slog.String("lua-version", lua.LUA_VERSION))) + h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) + return + } + + // Получаем глобальную переменную Result (таблица) + L.GetGlobal("Result") + if L.IsTable(-1) { + out := make(map[string]interface{}) + + L.PushNil() // Первый ключ + for { + if L.Next(-2) == 0 { + break + } + // На стеке: -1 = value, -2 = key + key := L.ToString(-2) + var val interface{} + + switch L.Type(-1) { + case lua.LUA_TSTRING: + val = L.ToString(-1) + case lua.LUA_TNUMBER: + val = L.ToNumber(-1) + case lua.LUA_TBOOLEAN: + val = L.ToBoolean(-1) + default: + // fallback + val = L.ToString(-1) + } + out[key] = val + L.Pop(1) // Удаляем value, key остаётся для следующего L.Next + } + L.Pop(1) // Удаляем таблицу Result со стека + + h.w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(h.w).Encode(out); err != nil { + log.Error("Failed to encode JSON response", + slog.String("error", err.Error())) + } + + switch out["status"] { + case "error": + log.Info("Command executed with error", + slog.String("cmd", cmd), + slog.Any("result", out)) + case "ok": + log.Info("Command executed successfully", + slog.String("cmd", cmd), slog.Any("result", out)) + default: + log.Info("Command executed and returned an unknown status", + slog.String("cmd", cmd), + slog.Any("result", out)) + } + } else { + L.Pop(1) // убираем не таблицу из стека + log.Error("Lua global 'Result' is not a table") + h.writeJSONError(http.StatusInternalServerError, "'Result' is not a table") + return + } + + log.Info("Session completed", + slog.Group("lua-status", + slog.String("lua-version", lua.LUA_VERSION))) +} diff --git a/sv1/handle_list.go b/core/sv1/handle_list.go similarity index 100% rename from sv1/handle_list.go rename to core/sv1/handle_list.go diff --git a/sv1/server.go b/core/sv1/server.go similarity index 96% rename from sv1/server.go rename to core/sv1/server.go index aa3473d..605bb47 100644 --- a/sv1/server.go +++ b/core/sv1/server.go @@ -5,7 +5,7 @@ import ( "net/http" "regexp" - "github.com/akyaiy/GoSally-mvp/config" + "github.com/akyaiy/GoSally-mvp/core/config" ) type ServerV1UtilsContract interface { diff --git a/sv1/utils.go b/core/sv1/utils.go similarity index 100% rename from sv1/utils.go rename to core/sv1/utils.go diff --git a/go.mod b/go.mod index 6111cc9..8cfedc4 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( require ( github.com/BurntSushi/toml v1.5.0 // indirect + github.com/aarzilli/golua v0.0.0-20250217091409-248753f411c4 // indirect github.com/joho/godotenv v1.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect diff --git a/go.sum b/go.sum index 92f3ef5..84c7419 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/aarzilli/golua v0.0.0-20250217091409-248753f411c4 h1:gW5i3FQAMcbkNgo/A87gCKAbBMalAO8BlPIMo9Gk2Ow= +github.com/aarzilli/golua v0.0.0-20250217091409-248753f411c4/go.mod h1:hMjfaJVSqVnxenMlsxrq3Ni+vrm9Hs64tU4M7dhUoO4= github.com/akyaiy/GoSally-mvp/config v0.0.0-20250622141207-5326dd45b694 h1:SJfxaud4HMVg9roTMMJaTQ+Odoz1LIw60TiS97EtWCE= github.com/akyaiy/GoSally-mvp/config v0.0.0-20250622141207-5326dd45b694/go.mod h1:2eaoBiPQmvZoC9DAzn11zHzWmscBI4dMTi4HeGO96XQ= github.com/akyaiy/GoSally-mvp/logs v0.0.0-20250622141207-5326dd45b694 h1:qYZIzX3NczqozwCnlLQ5M1vTLoCqIIF1qxxweub4zQo= diff --git a/sv1/handle_com.go b/sv1/handle_com.go deleted file mode 100644 index 3eb7298..0000000 --- a/sv1/handle_com.go +++ /dev/null @@ -1,133 +0,0 @@ -package sv1 - -import ( - "encoding/json" - "log/slog" - "net/http" - "os" - "path/filepath" - - "github.com/go-chi/chi/v5" - lua "github.com/yuin/gopher-lua" -) - -func (h *HandlerV1) _handle() { - uuid16 := h.newUUID() - log := h.log.With( - slog.Group("request", - slog.String("version", h.GetVersion()), - slog.String("url", h.r.URL.String()), - slog.String("method", h.r.Method), - ), - slog.Group("connection", - slog.String("connection-uuid", uuid16), - slog.String("remote", h.r.RemoteAddr), - ), - ) - log.Info("Received request") - - cmd := chi.URLParam(h.r, "cmd") - var scriptPath string - if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) { - log.Error("HTTP request error", - slog.String("error", "invalid command"), - slog.String("cmd", cmd), - slog.Int("status", http.StatusBadRequest)) - h.writeJSONError(http.StatusBadRequest, "invalid command") - return - } - if !h.listAllowedCmd.MatchString(cmd) { - log.Error("HTTP request error", - slog.String("error", "invalid command"), - slog.String("cmd", cmd), - slog.Int("status", http.StatusBadRequest)) - h.writeJSONError(http.StatusBadRequest, "invalid command") - return - } - if scriptPath = h.comMatch(chi.URLParam(h.r, "ver"), cmd); scriptPath == "" { - log.Error("HTTP request error", - slog.String("error", "command not found"), - slog.String("cmd", cmd), - slog.Int("status", http.StatusNotFound)) - h.writeJSONError(http.StatusNotFound, "command not found") - return - } - - scriptPath = filepath.Join(h.cfg.ComDir, scriptPath) - if _, err := os.Stat(scriptPath); err != nil { - log.Error("HTTP request error", - slog.String("error", "command not found"), - slog.String("cmd", cmd), - slog.Int("status", http.StatusNotFound)) - h.writeJSONError(http.StatusNotFound, "command not found") - return - } - - L := lua.NewState() - defer L.Close() - - L.OpenLibs() // loads base, io, os, string, math, table, debug, package, coroutine, channel… :contentReference[oaicite:0]{index=0} - - qt := h.r.URL.Query() - tbl := L.NewTable() - for k, v := range qt { - if len(v) > 0 { - L.SetField(tbl, k, lua.LString(v[0])) - } - } - L.SetGlobal("Params", tbl) - L.SetGlobal("Result", L.NewTable()) - - prepareLuaEnv := filepath.Join(h.cfg.ComDir, "_prepare"+".lua") - if _, err := os.Stat(prepareLuaEnv); err == nil { - if err := L.DoFile(prepareLuaEnv); err != nil { - log.Error("Failed to prepare lua environment", - slog.String("error", err.Error())) - h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) - return - } - } else { - log.Error("No environment preparation script found, skipping preparation", - slog.String("error", err.Error())) - } - - if err := L.DoFile(scriptPath); err != nil { - log.Error("Failed to execute lua script", - slog.String("error", err.Error())) - h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) - return - } - - out := make(map[string]any) - if rt := L.GetGlobal("Result"); rt.Type() == lua.LTTable { - rt.(*lua.LTable).ForEach(func(k, v lua.LValue) { - switch v.Type() { - case lua.LTString: - out[k.String()] = v.String() - case lua.LTNumber: - out[k.String()] = float64(v.(lua.LNumber)) - case lua.LTBool: - out[k.String()] = bool(v.(lua.LBool)) - default: - out[k.String()] = v.String() - } - }) - } - - h.w.Header().Set("Content-Type", "application/json") - json.NewEncoder(h.w).Encode(out) - switch out["status"] { - case "error": - log.Info("Command executed with error", - slog.String("cmd", cmd), - slog.Any("result", out)) - case "ok": - log.Info("Command executed successfully", - slog.String("cmd", cmd), slog.Any("result", out)) - default: - log.Info("Command executed and returned an unknown status", - slog.String("cmd", cmd), - slog.Any("result", out)) - } - log.Info("Session completed") -}