mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-03 17:52:24 +00:00
429 lines
14 KiB
Go
429 lines
14 KiB
Go
package sv1
|
||
|
||
import (
|
||
"log/slog"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"github.com/akyaiy/GoSally-mvp/internal/core/utils"
|
||
"github.com/akyaiy/GoSally-mvp/internal/server/rpc"
|
||
lua "github.com/yuin/gopher-lua"
|
||
)
|
||
|
||
func (h *HandlerV1) Handle(r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse {
|
||
if req.Method == "" {
|
||
h.log.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method))
|
||
return rpc.NewError(rpc.ErrMethodIsMissing, rpc.ErrMethodIsMissingS, req.ID)
|
||
}
|
||
|
||
method, err := h.resolveMethodPath(req.Method)
|
||
if err != nil {
|
||
if err.Error() == rpc.ErrInvalidMethodFormatS {
|
||
h.log.Info("invalid request received", slog.String("issue", rpc.ErrInvalidMethodFormatS), slog.String("requested-method", req.Method))
|
||
return rpc.NewError(rpc.ErrInvalidMethodFormat, rpc.ErrInvalidMethodFormatS, req.ID)
|
||
} else if err.Error() == rpc.ErrMethodNotFoundS {
|
||
h.log.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method))
|
||
return rpc.NewError(rpc.ErrMethodNotFound, rpc.ErrMethodNotFoundS, req.ID)
|
||
}
|
||
}
|
||
|
||
return h.HandleLUA(method, req)
|
||
}
|
||
|
||
func (h *HandlerV1) HandleLUA(path string, req *rpc.RPCRequest) *rpc.RPCResponse {
|
||
L := lua.NewState()
|
||
defer L.Close()
|
||
|
||
inTable := L.NewTable()
|
||
paramsTable := L.NewTable()
|
||
if fetchedParams, ok := req.Params.(map[string]interface{}); ok {
|
||
for k, v := range fetchedParams {
|
||
L.SetField(paramsTable, k, utils.ConvertGolangTypesToLua(L, v))
|
||
}
|
||
}
|
||
L.SetField(inTable, "Params", paramsTable)
|
||
L.SetGlobal("In", inTable)
|
||
|
||
outTable := L.NewTable()
|
||
resultTable := L.NewTable()
|
||
L.SetField(outTable, "Result", resultTable)
|
||
L.SetGlobal("Out", outTable)
|
||
|
||
prep := filepath.Join(h.cfg.ComDir, "_prepare.lua")
|
||
if _, err := os.Stat(prep); err == nil {
|
||
if err := L.DoFile(prep); err != nil {
|
||
return rpc.NewError(rpc.ErrInternalError, err.Error(), req.ID)
|
||
}
|
||
}
|
||
if err := L.DoFile(path); err != nil {
|
||
return rpc.NewError(rpc.ErrInternalError, err.Error(), req.ID)
|
||
}
|
||
|
||
lv := L.GetGlobal("Out")
|
||
outTbl, ok := lv.(*lua.LTable)
|
||
if !ok {
|
||
return rpc.NewError(rpc.ErrInternalError, "Out is not a table", req.ID)
|
||
}
|
||
|
||
// Check if Out.Error exists
|
||
if errVal := outTbl.RawGetString("Error"); errVal != lua.LNil {
|
||
if errTbl, ok := errVal.(*lua.LTable); ok {
|
||
code := rpc.ErrInternalError
|
||
message := "Internal script error"
|
||
if c := errTbl.RawGetString("code"); c.Type() == lua.LTNumber {
|
||
code = int(c.(lua.LNumber))
|
||
}
|
||
if msg := errTbl.RawGetString("message"); msg.Type() == lua.LTString {
|
||
message = msg.String()
|
||
}
|
||
return rpc.NewError(code, message, req.ID)
|
||
}
|
||
return rpc.NewError(rpc.ErrInternalError, "Out.Error is not a table", req.ID)
|
||
}
|
||
|
||
// Otherwise, parse Out.Result
|
||
resultVal := outTbl.RawGetString("Result")
|
||
resultTbl, ok := resultVal.(*lua.LTable)
|
||
if !ok {
|
||
return rpc.NewError(rpc.ErrInternalError, "Out.Result is not a table", req.ID)
|
||
}
|
||
|
||
out := make(map[string]any)
|
||
resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||
out[key.String()] = utils.ConvertLuaTypesToGolang(value)
|
||
})
|
||
return rpc.NewResponse(out, req.ID)
|
||
}
|
||
|
||
// func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) {
|
||
// var req PettiRequest
|
||
// // server, ok := s.servers[serversApiVer(payload.PettiVer)]
|
||
// // if !ok {
|
||
// // WriteRouterError(w, &RouterError{
|
||
// // Status: "error",
|
||
// // StatusCode: http.StatusBadRequest,
|
||
// // Payload: map[string]any{
|
||
// // "Message": InvalidProtovolVersion,
|
||
// // },
|
||
// // })
|
||
// // s.log.Info("invalid request received", slog.String("issue", InvalidProtovolVersion), slog.String("requested-version", payload.PettiVer))
|
||
// // return
|
||
// // }
|
||
// if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "невалидный JSON: "+err.Error())
|
||
// return
|
||
// }
|
||
|
||
// if req.PettiVer == "" {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "отсутствует PettiVer")
|
||
// return
|
||
// }
|
||
// if req.PettiVer != h.GetVersion() {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "неподдерживаемая версия PettiVer")
|
||
// return
|
||
// }
|
||
// if req.PackageType.Request == "" {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "отсутствует PackageType.Request")
|
||
// return
|
||
// }
|
||
// if req.Payload == nil {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "отсутствует Payload")
|
||
// return
|
||
// }
|
||
// cmdRaw, ok := req.Payload["Exec"].(string)
|
||
// if !ok || cmdRaw == "" {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "Payload.Exec отсутствует или некорректен")
|
||
// return
|
||
// }
|
||
// cmd := cmdRaw
|
||
|
||
// if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) || !h.listAllowedCmd.MatchString(cmd) {
|
||
// utils.WriteJSONError(w, http.StatusBadRequest, "команда запрещена")
|
||
// return
|
||
// }
|
||
|
||
// // ===== Проверка скрипта
|
||
// scriptPath := h.comMatch(h.GetVersion(), cmd)
|
||
// if scriptPath == "" {
|
||
// utils.WriteJSONError(w, http.StatusNotFound, "команда не найдена")
|
||
// return
|
||
// }
|
||
// fullPath := filepath.Join(h.cfg.ComDir, scriptPath)
|
||
// if _, err := os.Stat(fullPath); err != nil {
|
||
// utils.WriteJSONError(w, http.StatusNotFound, "файл команды не найден")
|
||
// return
|
||
// }
|
||
|
||
// // ===== Запуск Lua
|
||
// L := lua.NewState()
|
||
// defer L.Close()
|
||
|
||
// inTable := L.NewTable()
|
||
// paramsTable := L.NewTable()
|
||
// if params, ok := req.Payload["PassedParameters"].(map[string]interface{}); ok {
|
||
// for k, v := range params {
|
||
// L.SetField(paramsTable, k, utils.ConvertGolangTypesToLua(L, v))
|
||
// }
|
||
// }
|
||
// L.SetField(inTable, "Params", paramsTable)
|
||
// L.SetGlobal("In", inTable)
|
||
|
||
// resultTable := L.NewTable()
|
||
// outTable := L.NewTable()
|
||
// L.SetField(outTable, "Result", resultTable)
|
||
// L.SetGlobal("Out", outTable)
|
||
|
||
// prepareLua := filepath.Join(h.cfg.ComDir, "_prepare.lua")
|
||
// if _, err := os.Stat(prepareLua); err == nil {
|
||
// if err := L.DoFile(prepareLua); err != nil {
|
||
// utils.WriteJSONError(w, http.StatusInternalServerError, "lua _prepare ошибка: "+err.Error())
|
||
// return
|
||
// }
|
||
// }
|
||
// if err := L.DoFile(fullPath); err != nil {
|
||
// utils.WriteJSONError(w, http.StatusInternalServerError, "lua exec ошибка: "+err.Error())
|
||
// return
|
||
// }
|
||
|
||
// lv := L.GetGlobal("Out")
|
||
// tbl, ok := lv.(*lua.LTable)
|
||
// if !ok {
|
||
// utils.WriteJSONError(w, http.StatusInternalServerError, "'Out' не таблица")
|
||
// return
|
||
// }
|
||
// resultVal := tbl.RawGetString("Result")
|
||
// resultTbl, ok := resultVal.(*lua.LTable)
|
||
// if !ok {
|
||
// utils.WriteJSONError(w, http.StatusInternalServerError, "'Result' не таблица")
|
||
// return
|
||
// }
|
||
|
||
// out := make(map[string]any)
|
||
// resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||
// out[key.String()] = utils.ConvertLuaTypesToGolang(value)
|
||
// })
|
||
|
||
// uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.MetaDir, "uuid"))
|
||
|
||
// resp := PettiResponse{
|
||
// PettiVer: req.PettiVer,
|
||
// ResponsibleAgentUUID: uuid32,
|
||
// PackageType: struct {
|
||
// AnswerOf string `json:"AnswerOf"`
|
||
// }{AnswerOf: req.PackageType.Request},
|
||
// Payload: map[string]any{
|
||
// "RequestedCommand": cmd,
|
||
// "Response": out,
|
||
// },
|
||
// }
|
||
|
||
// // ===== Финальная проверка на сериализацию (валидность сборки)
|
||
// respData, err := json.Marshal(resp)
|
||
// if err != nil {
|
||
// utils.WriteJSONError(w, http.StatusInternalServerError, "внутренняя ошибка: пакет невалиден")
|
||
// return
|
||
// }
|
||
|
||
// w.Header().Set("Content-Type", "application/json")
|
||
// w.WriteHeader(http.StatusOK)
|
||
// if _, err := w.Write(respData); err != nil {
|
||
// h.log.Error("Ошибка при отправке JSON", slog.String("err", err.Error()))
|
||
// }
|
||
|
||
// // ===== Логгирование статуса
|
||
// status, _ := out["status"].(string)
|
||
// switch status {
|
||
// case "ok":
|
||
// h.log.Info("Успешно", slog.String("cmd", cmd), slog.Any("out", out))
|
||
// case "error":
|
||
// h.log.Warn("Ошибка в команде", slog.String("cmd", cmd), slog.Any("out", out))
|
||
// default:
|
||
// h.log.Info("Неизвестный статус", slog.String("cmd", cmd), slog.Any("out", out))
|
||
// }
|
||
// }
|
||
|
||
/*
|
||
import (
|
||
"encoding/json"
|
||
"log/slog"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"github.com/akyaiy/GoSally-mvp/core/config"
|
||
"github.com/akyaiy/GoSally-mvp/core/corestate"
|
||
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||
"github.com/go-chi/chi/v5"
|
||
lua "github.com/yuin/gopher-lua"
|
||
)
|
||
|
||
// HandlerV1 is the main handler for version 1 of the API.
|
||
// The function processes the HTTP request and runs Lua scripts,
|
||
// preparing the environment and subsequently transmitting the execution result
|
||
func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) {
|
||
uuid16, err := utils.NewUUID(int(config.UUIDLength))
|
||
if err != nil {
|
||
h.log.Error("Failed to generate UUID",
|
||
slog.String("error", err.Error()))
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error()); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
log := h.log.With(
|
||
slog.Group("request",
|
||
slog.String("version", h.GetVersion()),
|
||
slog.String("url", r.URL.String()),
|
||
slog.String("method", r.Method),
|
||
),
|
||
slog.Group("connection",
|
||
slog.String("connection-uuid", uuid16),
|
||
slog.String("remote", r.RemoteAddr),
|
||
),
|
||
)
|
||
log.Info("Received request")
|
||
|
||
cmd := chi.URLParam(r, "cmd")
|
||
if !h.allowedCmd.MatchString(string([]rune(cmd)[0])) || !h.listAllowedCmd.MatchString(cmd) {
|
||
log.Error("HTTP request error",
|
||
slog.String("error", "invalid command"),
|
||
slog.String("cmd", cmd),
|
||
slog.Int("status", http.StatusBadRequest))
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusBadRequest, "invalid command"); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
scriptPath := h.comMatch(chi.URLParam(r, "ver"), cmd)
|
||
if scriptPath == "" {
|
||
log.Error("HTTP request error",
|
||
slog.String("error", "command not found"),
|
||
slog.String("cmd", cmd),
|
||
slog.Int("status", http.StatusNotFound))
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusNotFound, "command not found"); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
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))
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusNotFound, "command not found"); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
L := lua.NewState()
|
||
defer L.Close()
|
||
|
||
paramsTable := L.NewTable()
|
||
qt := r.URL.Query()
|
||
for k, v := range qt {
|
||
if len(v) > 0 {
|
||
L.SetField(paramsTable, k, lua.LString(v[0]))
|
||
}
|
||
}
|
||
inTable := L.NewTable()
|
||
L.SetField(inTable, "Params", paramsTable)
|
||
L.SetGlobal("In", inTable)
|
||
|
||
// Создаем таблицу Out с Result
|
||
resultTable := L.NewTable()
|
||
outTable := L.NewTable()
|
||
L.SetField(outTable, "Result", resultTable)
|
||
L.SetGlobal("Out", outTable)
|
||
|
||
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()))
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusInternalServerError, "lua error: "+err.Error()); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
} else {
|
||
log.Warn("No environment preparation script found, skipping preparation")
|
||
}
|
||
|
||
if err := L.DoFile(scriptPath); err != nil {
|
||
log.Error("Failed to execute lua script",
|
||
slog.String("error", err.Error()))
|
||
if err := utils.WriteJSONError(w, http.StatusInternalServerError, "lua error: "+err.Error()); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
lv := L.GetGlobal("Out")
|
||
tbl, ok := lv.(*lua.LTable)
|
||
if !ok {
|
||
log.Error("Lua global 'Out' is not a table")
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusInternalServerError, "'Out' is not a table"); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
resultVal := tbl.RawGetString("Result")
|
||
resultTbl, ok := resultVal.(*lua.LTable)
|
||
if !ok {
|
||
log.Error("Lua global 'Result' is not a table")
|
||
|
||
if err := utils.WriteJSONError(w, http.StatusInternalServerError, "'Result' is not a table"); err != nil {
|
||
h.log.Error("Failed to write JSON", slog.String("err", err.Error()))
|
||
}
|
||
return
|
||
}
|
||
|
||
out := make(map[string]any)
|
||
resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||
out[key.String()] = utils.ConvertLuaTypesToGolang(value)
|
||
})
|
||
uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.MetaDir, "uuid"))
|
||
response := ResponseFormat{
|
||
ResponsibleAgentUUID: uuid32,
|
||
RequestedCommand: cmd,
|
||
Response: out,
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||
log.Error("Failed to encode JSON response",
|
||
slog.String("error", err.Error()))
|
||
}
|
||
|
||
status, _ := out["status"].(string)
|
||
switch 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")
|
||
}
|
||
*/
|