mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-03 20:12:25 +00:00
Enhance server functionality: add versioning support, implement command handling improvements, and introduce new Lua scripts for command execution
This commit is contained in:
@@ -4,5 +4,10 @@ http_server:
|
|||||||
address: "localhost:8080"
|
address: "localhost:8080"
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
idle_timeout: 30s
|
idle_timeout: 30s
|
||||||
|
api:
|
||||||
|
latest-version: v1
|
||||||
|
layers:
|
||||||
|
- b1
|
||||||
|
- s2
|
||||||
|
|
||||||
com_dir: "com/"
|
com_dir: "com/"
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/akyaiy/GoSally-mvp/config"
|
"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/logs"
|
||||||
"github.com/akyaiy/GoSally-mvp/sv1"
|
"github.com/akyaiy/GoSally-mvp/sv1"
|
||||||
|
|
||||||
@@ -31,18 +32,18 @@ func main() {
|
|||||||
Config: cfg,
|
Config: cfg,
|
||||||
AllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9]+$`),
|
AllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9]+$`),
|
||||||
ListAllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9_-]+$`),
|
ListAllowedCmd: regexp.MustCompile(`^[a-zA-Z0-9_-]+$`),
|
||||||
|
Ver: "v1",
|
||||||
})
|
})
|
||||||
|
s := gs.InitGeneral(&gs.GeneralServerInit{
|
||||||
|
Log: *logs.SetupLogger(cfg.Mode),
|
||||||
|
Config: cfg,
|
||||||
|
}, serverv1)
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Route("/v1/com", func(r chi.Router) {
|
r.Route("/{ver}/com", func(r chi.Router) {
|
||||||
r.Get("/", serverv1.HandleList)
|
r.Get("/", s.HandleList)
|
||||||
r.Get("/{cmd}", serverv1.Handle)
|
r.Get("/{cmd}", s.Handle)
|
||||||
})
|
})
|
||||||
// r.Route("/v2/com", func(r chi.Router) {
|
|
||||||
// r.Get("/", handleV1ComList)
|
|
||||||
// r.Get("/{cmd}", handleV1)
|
|
||||||
// })
|
|
||||||
r.NotFound(serverv1.ErrNotFound)
|
r.NotFound(serverv1.ErrNotFound)
|
||||||
log.Info("Server started", slog.String("address", cfg.Address))
|
log.Info("Server started", slog.String("address", cfg.Address))
|
||||||
http.ListenAndServe(cfg.Address, r)
|
http.ListenAndServe(cfg.Address, r)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,3 @@ Params = {}
|
|||||||
|
|
||||||
---@type AnyTable
|
---@type AnyTable
|
||||||
Result = {}
|
Result = {}
|
||||||
|
|
||||||
---@type AnyTable
|
|
||||||
Me = {}
|
|
||||||
5
com/_prepare.lua
Normal file
5
com/_prepare.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
print = function() end
|
||||||
|
io.write = function(...) end
|
||||||
|
io.stdout = function() return nil end
|
||||||
|
io.stderr = function() return nil end
|
||||||
|
io.read = function(...) return nil end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--- #description = "Echoes back the message provided in the 'msg' parameter."
|
--- #description = "Echoes back the message provided in the 'msg' parameter. b1"
|
||||||
|
|
||||||
if not Params.msg then
|
if not Params.msg then
|
||||||
Result.status = "error"
|
Result.status = "error"
|
||||||
@@ -18,6 +18,12 @@ type HTTPServer struct {
|
|||||||
Address string `yaml:"address" env-default:"0.0.0.0:8080"`
|
Address string `yaml:"address" env-default:"0.0.0.0:8080"`
|
||||||
Timeout time.Duration `yaml:"timeout" env-default:"5s"`
|
Timeout time.Duration `yaml:"timeout" env-default:"5s"`
|
||||||
IdleTimeout time.Duration `yaml:"idle_timeout" env-default:"60s"`
|
IdleTimeout time.Duration `yaml:"idle_timeout" env-default:"60s"`
|
||||||
|
HTTPServer_Api `yaml:"api"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPServer_Api struct {
|
||||||
|
LatestVer string `yaml:"latest-version" env-required:"true"`
|
||||||
|
Layers []string `yaml:"layers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigEnv struct {
|
type ConfigEnv struct {
|
||||||
|
|||||||
159
general_server/hanle_multi.go
Normal file
159
general_server/hanle_multi.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package general_server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/akyaiy/GoSally-mvp/config"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serversApiVer string
|
||||||
|
|
||||||
|
type GeneralServerApiContract interface {
|
||||||
|
GetVersion() string
|
||||||
|
|
||||||
|
Handle(w http.ResponseWriter, r *http.Request)
|
||||||
|
HandleList(w http.ResponseWriter, r *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralServerContarct interface {
|
||||||
|
GeneralServerApiContract
|
||||||
|
AppendToArray(GeneralServerApiContract) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralServer struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
r *http.Request
|
||||||
|
|
||||||
|
servers map[serversApiVer]GeneralServerApiContract
|
||||||
|
|
||||||
|
log slog.Logger
|
||||||
|
cfg *config.ConfigConf
|
||||||
|
}
|
||||||
|
|
||||||
|
// structure only for initialization
|
||||||
|
type GeneralServerInit struct {
|
||||||
|
Log slog.Logger
|
||||||
|
Config *config.ConfigConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitGeneral(o *GeneralServerInit, servers ...GeneralServerApiContract) *GeneralServer {
|
||||||
|
general := &GeneralServer{
|
||||||
|
servers: make(map[serversApiVer]GeneralServerApiContract),
|
||||||
|
cfg: o.Config,
|
||||||
|
log: o.Log,
|
||||||
|
}
|
||||||
|
for _, s := range servers {
|
||||||
|
general.servers[serversApiVer(s.GetVersion())] = s
|
||||||
|
}
|
||||||
|
return general
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) GetVersion() string {
|
||||||
|
return "general"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) AppendToArray(server GeneralServerApiContract) error {
|
||||||
|
if _, exist := s.servers[serversApiVer(server.GetVersion())]; !exist {
|
||||||
|
s.servers[serversApiVer(server.GetVersion())] = server
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("server with this version is already exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.w = w
|
||||||
|
s.r = r
|
||||||
|
serverReqApiVer := chi.URLParam(r, "ver")
|
||||||
|
|
||||||
|
s.log.Info("Received request",
|
||||||
|
slog.String("remote", r.RemoteAddr),
|
||||||
|
slog.String("method", r.Method),
|
||||||
|
slog.String("url", r.URL.String()),
|
||||||
|
slog.String("requested-version", serverReqApiVer),
|
||||||
|
)
|
||||||
|
|
||||||
|
if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok {
|
||||||
|
srv.Handle(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
slog.String("layer", serverReqApiVer),
|
||||||
|
slog.String("fallback-version", s.cfg.LatestVer),
|
||||||
|
)
|
||||||
|
srv.Handle(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Error("HTTP request error: unsupported API version",
|
||||||
|
slog.String("remote", s.r.RemoteAddr),
|
||||||
|
slog.String("method", s.r.Method),
|
||||||
|
slog.String("url", s.r.URL.String()),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
|
s.writeJSONError(http.StatusBadRequest, "unsupported API version")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.w = w
|
||||||
|
s.r = r
|
||||||
|
serverReqApiVer := chi.URLParam(r, "ver")
|
||||||
|
|
||||||
|
s.log.Info("Received request",
|
||||||
|
slog.String("remote", r.RemoteAddr),
|
||||||
|
slog.String("method", r.Method),
|
||||||
|
slog.String("url", r.URL.String()),
|
||||||
|
slog.String("requested-version", serverReqApiVer),
|
||||||
|
)
|
||||||
|
|
||||||
|
if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok {
|
||||||
|
srv.HandleList(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
slog.String("layer", serverReqApiVer),
|
||||||
|
slog.String("fallback-version", s.cfg.LatestVer),
|
||||||
|
)
|
||||||
|
srv.HandleList(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Error("HTTP request error: unsupported API version",
|
||||||
|
slog.String("remote", s.r.RemoteAddr),
|
||||||
|
slog.String("method", s.r.Method),
|
||||||
|
slog.String("url", s.r.URL.String()),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
|
s.writeJSONError(http.StatusBadRequest, "unsupported API version")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) _errNotFound() {
|
||||||
|
s.writeJSONError(http.StatusBadRequest, "invalid request")
|
||||||
|
s.log.Error("HTTP request error",
|
||||||
|
slog.String("remote", s.r.RemoteAddr),
|
||||||
|
slog.String("method", s.r.Method),
|
||||||
|
slog.String("url", s.r.URL.String()),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GeneralServer) writeJSONError(status int, msg string) {
|
||||||
|
s.w.Header().Set("Content-Type", "application/json")
|
||||||
|
s.w.WriteHeader(status)
|
||||||
|
resp := map[string]interface{}{
|
||||||
|
"status": "error",
|
||||||
|
"error": msg,
|
||||||
|
"code": status,
|
||||||
|
}
|
||||||
|
json.NewEncoder(s.w).Encode(resp)
|
||||||
|
}
|
||||||
@@ -13,23 +13,51 @@ import (
|
|||||||
|
|
||||||
func (h *HandlerV1) _handle() {
|
func (h *HandlerV1) _handle() {
|
||||||
uuid16 := h.newUUID()
|
uuid16 := h.newUUID()
|
||||||
h.log.Info("Received request", slog.String("version", "v1"), slog.String("connection-uuid", uuid16), slog.String("remote", h.r.RemoteAddr), slog.String("method", h.r.Method), slog.String("url", h.r.URL.String()))
|
h.log.Info("Received request",
|
||||||
|
slog.String("version", "v1"),
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
|
slog.String("method", h.r.Method),
|
||||||
|
slog.String("url", h.r.URL.String()))
|
||||||
|
|
||||||
cmd := chi.URLParam(h.r, "cmd")
|
cmd := chi.URLParam(h.r, "cmd")
|
||||||
|
var scriptPath string
|
||||||
if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) {
|
if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) {
|
||||||
|
h.log.Error("HTTP request error",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("error", "invalid command"),
|
||||||
|
slog.String("cmd", cmd),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
h.writeJSONError(http.StatusBadRequest, "invalid command")
|
h.writeJSONError(http.StatusBadRequest, "invalid command")
|
||||||
h.log.Error("HTTP request error", slog.String("connection-uuid", uuid16), slog.String("error", "invalid command"), slog.String("cmd", cmd), slog.Int("status", http.StatusBadRequest))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !h.listAllowedCmd.MatchString(cmd) {
|
if !h.listAllowedCmd.MatchString(cmd) {
|
||||||
|
h.log.Error("HTTP request error",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("error", "invalid command"),
|
||||||
|
slog.String("cmd", cmd),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
h.writeJSONError(http.StatusBadRequest, "invalid command")
|
h.writeJSONError(http.StatusBadRequest, "invalid command")
|
||||||
h.log.Error("HTTP request error", slog.String("connection-uuid", uuid16), slog.String("error", "invalid command"), slog.String("cmd", cmd), slog.Int("status", http.StatusBadRequest))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
scriptPath := filepath.Join(h.cfg.ComDir, cmd+".lua")
|
if scriptPath = h.comMatch(chi.URLParam(h.r, "ver"), cmd); scriptPath == "" {
|
||||||
if _, err := os.Stat(scriptPath); err != nil {
|
h.log.Error("HTTP request error",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
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 {
|
||||||
|
h.log.Error("HTTP request error",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("error", "command not found"),
|
||||||
|
slog.String("cmd", cmd),
|
||||||
|
slog.Int("status", http.StatusNotFound))
|
||||||
h.writeJSONError(http.StatusNotFound, "command not found")
|
h.writeJSONError(http.StatusNotFound, "command not found")
|
||||||
h.log.Error("HTTP request error", slog.String("connection-uuid", uuid16), slog.String("error", "command not found"), slog.String("cmd", cmd), slog.Int("status", http.StatusNotFound))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,17 +76,24 @@ func (h *HandlerV1) _handle() {
|
|||||||
L.SetGlobal("Params", tbl)
|
L.SetGlobal("Params", tbl)
|
||||||
L.SetGlobal("Result", L.NewTable())
|
L.SetGlobal("Result", L.NewTable())
|
||||||
|
|
||||||
L.DoString(`
|
prepareLuaEnv := filepath.Join(h.cfg.ComDir, "_prepare"+".lua")
|
||||||
print = function() end
|
if _, err := os.Stat(prepareLuaEnv); err == nil {
|
||||||
io.write = function(...) end
|
if err := L.DoFile(prepareLuaEnv); err != nil {
|
||||||
io.stdout = function() return nil end
|
h.log.Error("Failed to prepare lua environment",
|
||||||
io.stderr = function() return nil end
|
slog.String("connection-uuid", uuid16),
|
||||||
io.read = function(...) return nil end
|
slog.String("error", err.Error()))
|
||||||
`)
|
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.log.Error("No environment preparation script found, skipping preparation", slog.String("connection-uuid", uuid16), slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
if err := L.DoFile(scriptPath); err != nil {
|
if err := L.DoFile(scriptPath); err != nil {
|
||||||
|
h.log.Error("Failed to execute lua script",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("error", err.Error()))
|
||||||
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
|
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
|
||||||
h.log.Error("Failed to execute lua script", slog.String("connection-uuid", uuid16), slog.String("error", err.Error()))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +117,23 @@ func (h *HandlerV1) _handle() {
|
|||||||
json.NewEncoder(h.w).Encode(out)
|
json.NewEncoder(h.w).Encode(out)
|
||||||
switch out["status"] {
|
switch out["status"] {
|
||||||
case "error":
|
case "error":
|
||||||
h.log.Info("Command executed with error", slog.String("connection-uuid", uuid16), slog.String("cmd", cmd), slog.Any("result", out))
|
h.log.Info("Command executed with error",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("cmd", cmd),
|
||||||
|
slog.Any("result", out))
|
||||||
case "ok":
|
case "ok":
|
||||||
h.log.Info("Command executed successfully", slog.String("connection-uuid", uuid16), slog.String("cmd", cmd), slog.Any("result", out))
|
h.log.Info("Command executed successfully",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("cmd", cmd), slog.Any("result", out))
|
||||||
default:
|
default:
|
||||||
h.log.Info("Command executed and returned an unknown status", slog.String("connection-uuid", uuid16), slog.String("cmd", cmd), slog.Any("result", out))
|
h.log.Info("Command executed and returned an unknown status",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("cmd", cmd),
|
||||||
|
slog.Any("result", out))
|
||||||
}
|
}
|
||||||
h.log.Info("Session completed", slog.String("connection-uuid", uuid16), slog.String("remote", h.r.RemoteAddr), slog.String("method", h.r.Method), slog.String("url", h.r.URL.String()))
|
h.log.Info("Session completed",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
|
slog.String("method", h.r.Method),
|
||||||
|
slog.String("url", h.r.URL.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,50 +6,103 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
_ "github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HandlerV1) _handleList() {
|
func (h *HandlerV1) _handleList() {
|
||||||
uuid16 := h.newUUID()
|
uuid16 := h.newUUID()
|
||||||
h.log.Info("Received request", slog.String("version", "v1"), slog.String("connection-uuid", uuid16), slog.String("remote", h.r.RemoteAddr), slog.String("method", h.r.Method), slog.String("url", h.r.URL.String()))
|
h.log.Info("Received request",
|
||||||
|
slog.String("version", "v1"),
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
|
slog.String("method", h.r.Method),
|
||||||
|
slog.String("url", h.r.URL.String()))
|
||||||
|
|
||||||
type ComMeta struct {
|
type ComMeta struct {
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
files []os.DirEntry
|
files []os.DirEntry
|
||||||
err error
|
err error
|
||||||
com ComMeta
|
|
||||||
commands = make(map[string]ComMeta)
|
commands = make(map[string]ComMeta)
|
||||||
|
cmdsProcessed = make(map[string]bool)
|
||||||
)
|
)
|
||||||
|
|
||||||
if files, err = os.ReadDir(h.cfg.ComDir); err != nil {
|
if files, err = os.ReadDir(h.cfg.ComDir); err != nil {
|
||||||
|
h.log.Error("Failed to read commands directory",
|
||||||
|
slog.String("error", err.Error()))
|
||||||
h.writeJSONError(http.StatusInternalServerError, "failed to read commands directory: "+err.Error())
|
h.writeJSONError(http.StatusInternalServerError, "failed to read commands directory: "+err.Error())
|
||||||
h.log.Error("Failed to read commands directory", slog.String("error", err.Error()))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apiVer := chi.URLParam(h.r, "ver")
|
||||||
|
|
||||||
|
// Сначала ищем версионные
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.IsDir() || filepath.Ext(file.Name()) != ".lua" {
|
if file.IsDir() || filepath.Ext(file.Name()) != ".lua" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cmdName := file.Name()[:len(file.Name())-4] // remove .lua extension
|
cmdFull := file.Name()[:len(file.Name())-4]
|
||||||
|
cmdParts := strings.SplitN(cmdFull, "?", 2)
|
||||||
|
cmdName := cmdParts[0]
|
||||||
|
|
||||||
if !h.allowedCmd.MatchString(string([]rune(cmdName)[0])) {
|
if !h.allowedCmd.MatchString(string([]rune(cmdName)[0])) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !h.listAllowedCmd.MatchString(cmdName) {
|
if !h.listAllowedCmd.MatchString(cmdName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if com.Description, err = h.extractDescriptionStatic(filepath.Join(h.cfg.ComDir, file.Name())); err != nil {
|
|
||||||
h.writeJSONError(http.StatusInternalServerError, "failed to read command: "+err.Error())
|
if len(cmdParts) == 2 && cmdParts[1] == apiVer {
|
||||||
h.log.Error("Failed to read command", slog.String("error", err.Error()))
|
description, _ := h.extractDescriptionStatic(filepath.Join(h.cfg.ComDir, file.Name()))
|
||||||
return
|
if description == "" {
|
||||||
|
description = "description missing"
|
||||||
}
|
}
|
||||||
if com.Description == "" {
|
commands[cmdName] = ComMeta{Description: description}
|
||||||
com.Description = "description missing"
|
cmdsProcessed[cmdName] = true
|
||||||
}
|
}
|
||||||
commands[cmdName] = ComMeta{Description: com.Description}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Потом фоллбеки
|
||||||
|
for _, file := range files {
|
||||||
|
if file.IsDir() || filepath.Ext(file.Name()) != ".lua" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmdFull := file.Name()[:len(file.Name())-4]
|
||||||
|
cmdParts := strings.SplitN(cmdFull, "?", 2)
|
||||||
|
cmdName := cmdParts[0]
|
||||||
|
|
||||||
|
if !h.allowedCmd.MatchString(string([]rune(cmdName)[0])) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !h.listAllowedCmd.MatchString(cmdName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cmdsProcessed[cmdName] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(cmdParts) == 1 {
|
||||||
|
description, _ := h.extractDescriptionStatic(filepath.Join(h.cfg.ComDir, file.Name()))
|
||||||
|
if description == "" {
|
||||||
|
description = "description missing"
|
||||||
|
}
|
||||||
|
commands[cmdName] = ComMeta{Description: description}
|
||||||
|
cmdsProcessed[cmdName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.log.Info("Command list prepared",
|
||||||
|
slog.String("connection-uuid", uuid16))
|
||||||
|
|
||||||
|
h.log.Info("Session completed",
|
||||||
|
slog.String("connection-uuid", uuid16),
|
||||||
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
|
slog.String("method", h.r.Method),
|
||||||
|
slog.String("url", h.r.URL.String()))
|
||||||
|
|
||||||
|
h.w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(h.w).Encode(commands)
|
json.NewEncoder(h.w).Encode(commands)
|
||||||
h.log.Info("Command executed successfully", slog.String("connection-uuid", uuid16))
|
|
||||||
h.log.Info("Session completed", slog.String("connection-uuid", uuid16), slog.String("remote", h.r.RemoteAddr), slog.String("method", h.r.Method), slog.String("url", h.r.URL.String()))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,9 @@ type ServerV1UtilsContract interface {
|
|||||||
ErrNotFound(w http.ResponseWriter, r *http.Request)
|
ErrNotFound(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerV1Contract interface {
|
|
||||||
ServerV1UtilsContract
|
|
||||||
|
|
||||||
Handle(w http.ResponseWriter, r *http.Request)
|
|
||||||
HandleList(w http.ResponseWriter, r *http.Request)
|
|
||||||
|
|
||||||
_handle()
|
|
||||||
_handleList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// structure only for initialization
|
// structure only for initialization
|
||||||
type HandlerV1InitStruct struct {
|
type HandlerV1InitStruct struct {
|
||||||
|
Ver string
|
||||||
Log slog.Logger
|
Log slog.Logger
|
||||||
Config *config.ConfigConf
|
Config *config.ConfigConf
|
||||||
AllowedCmd *regexp.Regexp
|
AllowedCmd *regexp.Regexp
|
||||||
@@ -45,6 +36,8 @@ type HandlerV1 struct {
|
|||||||
|
|
||||||
allowedCmd *regexp.Regexp
|
allowedCmd *regexp.Regexp
|
||||||
listAllowedCmd *regexp.Regexp
|
listAllowedCmd *regexp.Regexp
|
||||||
|
|
||||||
|
ver string
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
||||||
@@ -53,6 +46,7 @@ func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
|||||||
cfg: o.Config,
|
cfg: o.Config,
|
||||||
allowedCmd: o.AllowedCmd,
|
allowedCmd: o.AllowedCmd,
|
||||||
listAllowedCmd: o.ListAllowedCmd,
|
listAllowedCmd: o.ListAllowedCmd,
|
||||||
|
ver: o.Ver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,3 +61,7 @@ func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.r = r
|
h.r = r
|
||||||
h._handleList()
|
h._handleList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HandlerV1) GetVersion() string {
|
||||||
|
return h.ver
|
||||||
|
}
|
||||||
|
|||||||
37
sv1/utils.go
37
sv1/utils.go
@@ -28,7 +28,11 @@ func (h *HandlerV1) newUUID() string {
|
|||||||
|
|
||||||
func (h *HandlerV1) _errNotFound() {
|
func (h *HandlerV1) _errNotFound() {
|
||||||
h.writeJSONError(http.StatusBadRequest, "invalid request")
|
h.writeJSONError(http.StatusBadRequest, "invalid request")
|
||||||
h.log.Error("HTTP request error", slog.String("remote", h.r.RemoteAddr), slog.String("method", h.r.Method), slog.String("url", h.r.URL.String()), slog.Int("status", http.StatusBadRequest))
|
h.log.Error("HTTP request error",
|
||||||
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
|
slog.String("method", h.r.Method),
|
||||||
|
slog.String("url", h.r.URL.String()),
|
||||||
|
slog.Int("status", http.StatusBadRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HandlerV1) writeJSONError(status int, msg string) {
|
func (h *HandlerV1) writeJSONError(status int, msg string) {
|
||||||
@@ -55,3 +59,34 @@ func (h *HandlerV1) extractDescriptionStatic(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
return m[1], nil
|
return m[1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HandlerV1) comMatch(ver string, comName string) string {
|
||||||
|
files, err := os.ReadDir(h.cfg.ComDir)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("Failed to read com dir",
|
||||||
|
slog.String("error", err.Error()))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
baseName := comName + ".lua"
|
||||||
|
verName := comName + "?" + ver + ".lua"
|
||||||
|
|
||||||
|
var baseFileFound string
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fname := f.Name()
|
||||||
|
|
||||||
|
if fname == verName {
|
||||||
|
return fname
|
||||||
|
}
|
||||||
|
|
||||||
|
if fname == baseName {
|
||||||
|
baseFileFound = fname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseFileFound
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user