mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-03 08:32:24 +00:00
Refactor error handling and utility functions; remove deprecated code and improve logging
This commit is contained in:
@@ -63,7 +63,6 @@ func main() {
|
|||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.NotFound(serverv1.ErrNotFound)
|
|
||||||
|
|
||||||
address := cfg.Address
|
address := cfg.Address
|
||||||
if cfg.TlsEnabled {
|
if cfg.TlsEnabled {
|
||||||
|
|||||||
16
com/exec.lua
16
com/exec.lua
@@ -1,16 +0,0 @@
|
|||||||
if not Params.f then
|
|
||||||
Result.status = "error"
|
|
||||||
Result.error = "Missing parameter: f"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local code = os.execute("touch " .. Params.f)
|
|
||||||
if code ~= 0 then
|
|
||||||
Result.status = "error"
|
|
||||||
Result.message = "Failed to execute command"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
Result.status = "ok"
|
|
||||||
Result.message = "Command executed successfully"
|
|
||||||
@@ -5,13 +5,15 @@ var UUIDLength byte = 4
|
|||||||
|
|
||||||
// ApiRoute setting for go-chi for main route for api requests
|
// ApiRoute setting for go-chi for main route for api requests
|
||||||
var ApiRoute string = "/api/{ver}"
|
var ApiRoute string = "/api/{ver}"
|
||||||
|
|
||||||
// ComDirRoute setting for go-chi for main route for commands
|
// ComDirRoute setting for go-chi for main route for commands
|
||||||
var ComDirRoute string = "/com"
|
var ComDirRoute string = "/com"
|
||||||
|
|
||||||
// NodeVersion is the version of the node. It can be set by the build system or manually.
|
// NodeVersion is the version of the node. It can be set by the build system or manually.
|
||||||
// If not set, it will return "version0.0.0-none" by default
|
// If not set, it will return "version0.0.0-none" by default
|
||||||
var NodeVersion string
|
var NodeVersion string
|
||||||
// ActualFileName is a feature of the GoSally update system.
|
|
||||||
|
// ActualFileName is a feature of the GoSally update system.
|
||||||
// In the repository, the file specified in the variable contains the current information about updates
|
// In the repository, the file specified in the variable contains the current information about updates
|
||||||
var ActualFileName string = "actual.txt"
|
var ActualFileName string = "actual.txt"
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ func (_ _updateConsts) GetNodeVersion() string {
|
|||||||
}
|
}
|
||||||
func (_ _updateConsts) GetActualFileName() string { return ActualFileName }
|
func (_ _updateConsts) GetActualFileName() string { return ActualFileName }
|
||||||
|
|
||||||
func GetInternalConsts() _internalConsts { return _internalConsts{} }
|
func GetInternalConsts() _internalConsts { return _internalConsts{} }
|
||||||
func (_ _internalConsts) GetUUIDLength() byte { return UUIDLength }
|
func (_ _internalConsts) GetUUIDLength() byte { return UUIDLength }
|
||||||
|
|
||||||
func GetServerConsts() _serverConsts { return _serverConsts{} }
|
func GetServerConsts() _serverConsts { return _serverConsts{} }
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
package general_server
|
package general_server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/akyaiy/GoSally-mvp/core/config"
|
"github.com/akyaiy/GoSally-mvp/core/config"
|
||||||
|
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
@@ -140,7 +140,7 @@ func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
log.Error("HTTP request error: unsupported API version",
|
log.Error("HTTP request error: unsupported API version",
|
||||||
slog.Int("status", http.StatusBadRequest))
|
slog.Int("status", http.StatusBadRequest))
|
||||||
s.writeJSONError(http.StatusBadRequest, "unsupported API version")
|
utils.WriteJSONError(s.w, http.StatusBadRequest, "unsupported API version")
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleList processes incoming HTTP requests for listing commands, routing them to the appropriate server based on the API version.
|
// HandleList processes incoming HTTP requests for listing commands, routing them to the appropriate server based on the API version.
|
||||||
@@ -182,23 +182,5 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
log.Error("HTTP request error: unsupported API version",
|
log.Error("HTTP request error: unsupported API version",
|
||||||
slog.Int("status", http.StatusBadRequest))
|
slog.Int("status", http.StatusBadRequest))
|
||||||
s.writeJSONError(http.StatusBadRequest, "unsupported API version")
|
utils.WriteJSONError(s.w, http.StatusBadRequest, "unsupported API version")
|
||||||
}
|
|
||||||
|
|
||||||
// writeJSONError writes a JSON error response to the HTTP response writer.
|
|
||||||
// It sets the Content-Type to application/json, writes the specified HTTP status code,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
if err := json.NewEncoder(s.w).Encode(resp); err != nil {
|
|
||||||
s.log.Error("Failed to write JSON error response",
|
|
||||||
slog.String("error", err.Error()),
|
|
||||||
slog.Int("status", status))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// Package logs provides a logger setup function that configures the logger based on the environment.
|
||||||
|
// It supports different logging levels for development and production environments.
|
||||||
|
// It uses the standard library's slog package for structured logging.
|
||||||
package logs
|
package logs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -5,11 +8,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Environment constants for logger setup
|
||||||
const (
|
const (
|
||||||
envDev = "dev"
|
// envDev enables development logging with debug level
|
||||||
|
envDev = "dev"
|
||||||
|
// envProd enables production logging with info level
|
||||||
envProd = "prod"
|
envProd = "prod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetupLogger initializes and returns a logger based on the provided environment.
|
||||||
func SetupLogger(env string) *slog.Logger {
|
func SetupLogger(env string) *slog.Logger {
|
||||||
var log *slog.Logger
|
var log *slog.Logger
|
||||||
switch env {
|
switch env {
|
||||||
|
|||||||
@@ -6,30 +6,20 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MockHandler is a mock implementation of slog.Handler for testing purposes.
|
||||||
type MockHandler struct {
|
type MockHandler struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
// Logs stores the log records captured by the handler.
|
||||||
Logs []slog.Record
|
Logs []slog.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockHandler() *MockHandler {
|
func NewMockHandler() *MockHandler { return &MockHandler{} }
|
||||||
return &MockHandler{}
|
func (h *MockHandler) Enabled(_ context.Context, _ slog.Level) bool { return true }
|
||||||
}
|
func (h *MockHandler) WithAttrs(_ []slog.Attr) slog.Handler { return h }
|
||||||
|
func (h *MockHandler) WithGroup(_ string) slog.Handler { return h }
|
||||||
func (h *MockHandler) Enabled(_ context.Context, _ slog.Level) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *MockHandler) Handle(_ context.Context, r slog.Record) error {
|
func (h *MockHandler) Handle(_ context.Context, r slog.Record) error {
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
defer h.mu.Unlock()
|
||||||
h.Logs = append(h.Logs, r.Clone())
|
h.Logs = append(h.Logs, r.Clone())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MockHandler) WithAttrs(_ []slog.Attr) slog.Handler {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *MockHandler) WithGroup(_ string) slog.Handler {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,12 +7,22 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
lua "github.com/yuin/gopher-lua"
|
lua "github.com/yuin/gopher-lua"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HandlerV1) _handle() {
|
// HandlerV1 is the main handler for version 1 of the API.
|
||||||
uuid16 := h.newUUID()
|
// 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()
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("Failed to generate UUID",
|
||||||
|
slog.String("error", err.Error()))
|
||||||
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
log := h.log.With(
|
log := h.log.With(
|
||||||
slog.Group("request",
|
slog.Group("request",
|
||||||
slog.String("version", h.GetVersion()),
|
slog.String("version", h.GetVersion()),
|
||||||
@@ -32,7 +42,7 @@ func (h *HandlerV1) _handle() {
|
|||||||
slog.String("error", "invalid command"),
|
slog.String("error", "invalid command"),
|
||||||
slog.String("cmd", cmd),
|
slog.String("cmd", cmd),
|
||||||
slog.Int("status", http.StatusBadRequest))
|
slog.Int("status", http.StatusBadRequest))
|
||||||
h.writeJSONError(http.StatusBadRequest, "invalid command")
|
utils.WriteJSONError(h.w, http.StatusBadRequest, "invalid command")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +52,7 @@ func (h *HandlerV1) _handle() {
|
|||||||
slog.String("error", "command not found"),
|
slog.String("error", "command not found"),
|
||||||
slog.String("cmd", cmd),
|
slog.String("cmd", cmd),
|
||||||
slog.Int("status", http.StatusNotFound))
|
slog.Int("status", http.StatusNotFound))
|
||||||
h.writeJSONError(http.StatusNotFound, "command not found")
|
utils.WriteJSONError(h.w, http.StatusNotFound, "command not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,15 +62,13 @@ func (h *HandlerV1) _handle() {
|
|||||||
slog.String("error", "command not found"),
|
slog.String("error", "command not found"),
|
||||||
slog.String("cmd", cmd),
|
slog.String("cmd", cmd),
|
||||||
slog.Int("status", http.StatusNotFound))
|
slog.Int("status", http.StatusNotFound))
|
||||||
h.writeJSONError(http.StatusNotFound, "command not found")
|
utils.WriteJSONError(h.w, http.StatusNotFound, "command not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
L := lua.NewState()
|
L := lua.NewState()
|
||||||
defer L.Close()
|
defer L.Close()
|
||||||
|
|
||||||
// Создаем таблицу Params
|
|
||||||
// Создаем таблицу In с Params
|
|
||||||
paramsTable := L.NewTable()
|
paramsTable := L.NewTable()
|
||||||
qt := h.r.URL.Query()
|
qt := h.r.URL.Query()
|
||||||
for k, v := range qt {
|
for k, v := range qt {
|
||||||
@@ -78,49 +86,44 @@ func (h *HandlerV1) _handle() {
|
|||||||
L.SetField(outTable, "Result", resultTable)
|
L.SetField(outTable, "Result", resultTable)
|
||||||
L.SetGlobal("Out", outTable)
|
L.SetGlobal("Out", outTable)
|
||||||
|
|
||||||
// Скрипт подготовки окружения
|
|
||||||
prepareLuaEnv := filepath.Join(h.cfg.ComDir, "_prepare.lua")
|
prepareLuaEnv := filepath.Join(h.cfg.ComDir, "_prepare.lua")
|
||||||
if _, err := os.Stat(prepareLuaEnv); err == nil {
|
if _, err := os.Stat(prepareLuaEnv); err == nil {
|
||||||
if err := L.DoFile(prepareLuaEnv); err != nil {
|
if err := L.DoFile(prepareLuaEnv); err != nil {
|
||||||
log.Error("Failed to prepare lua environment",
|
log.Error("Failed to prepare lua environment",
|
||||||
slog.String("error", err.Error()))
|
slog.String("error", err.Error()))
|
||||||
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "lua error: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Warn("No environment preparation script found, skipping preparation")
|
log.Warn("No environment preparation script found, skipping preparation")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Основной Lua скрипт
|
|
||||||
if err := L.DoFile(scriptPath); err != nil {
|
if err := L.DoFile(scriptPath); err != nil {
|
||||||
log.Error("Failed to execute lua script",
|
log.Error("Failed to execute lua script",
|
||||||
slog.String("error", err.Error()))
|
slog.String("error", err.Error()))
|
||||||
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "lua error: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем Out
|
|
||||||
lv := L.GetGlobal("Out")
|
lv := L.GetGlobal("Out")
|
||||||
tbl, ok := lv.(*lua.LTable)
|
tbl, ok := lv.(*lua.LTable)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("Lua global 'Out' is not a table")
|
log.Error("Lua global 'Out' is not a table")
|
||||||
h.writeJSONError(http.StatusInternalServerError, "'Out' is not a table")
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "'Out' is not a table")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем Result из Out
|
|
||||||
resultVal := tbl.RawGetString("Result")
|
resultVal := tbl.RawGetString("Result")
|
||||||
resultTbl, ok := resultVal.(*lua.LTable)
|
resultTbl, ok := resultVal.(*lua.LTable)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("Lua global 'Result' is not a table")
|
log.Error("Lua global 'Result' is not a table")
|
||||||
h.writeJSONError(http.StatusInternalServerError, "'Result' is not a table")
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "'Result' is not a table")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Перебираем таблицу Result
|
|
||||||
out := make(map[string]interface{})
|
out := make(map[string]interface{})
|
||||||
resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||||||
out[key.String()] = convertTypes(value)
|
out[key.String()] = utils.ConvertLuaTypesToGolang(value)
|
||||||
})
|
})
|
||||||
|
|
||||||
h.w.Header().Set("Content-Type", "application/json")
|
h.w.Header().Set("Content-Type", "application/json")
|
||||||
@@ -147,26 +150,3 @@ func (h *HandlerV1) _handle() {
|
|||||||
|
|
||||||
log.Info("Session completed")
|
log.Info("Session completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTypes(value lua.LValue) any {
|
|
||||||
switch value.Type() {
|
|
||||||
case lua.LTString:
|
|
||||||
return value.String()
|
|
||||||
case lua.LTNumber:
|
|
||||||
return float64(value.(lua.LNumber))
|
|
||||||
case lua.LTBool:
|
|
||||||
return bool(value.(lua.LBool))
|
|
||||||
case lua.LTTable:
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
if tbl, ok := value.(*lua.LTable); ok {
|
|
||||||
tbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
|
||||||
result[key.String()] = convertTypes(value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
case lua.LTNil:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return value.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,11 +8,19 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HandlerV1) _handleList() {
|
// The function processes the HTTP request and returns a list of available commands.
|
||||||
uuid16 := h.newUUID()
|
func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
uuid16, err := utils.NewUUID()
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("Failed to generate UUID",
|
||||||
|
slog.String("error", err.Error()))
|
||||||
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to generate UUID: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
log := h.log.With(
|
log := h.log.With(
|
||||||
slog.Group("request",
|
slog.Group("request",
|
||||||
slog.String("version", h.GetVersion()),
|
slog.String("version", h.GetVersion()),
|
||||||
@@ -31,7 +39,6 @@ func (h *HandlerV1) _handleList() {
|
|||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
files []os.DirEntry
|
files []os.DirEntry
|
||||||
err error
|
|
||||||
commands = make(map[string]ComMeta)
|
commands = make(map[string]ComMeta)
|
||||||
cmdsProcessed = make(map[string]bool)
|
cmdsProcessed = make(map[string]bool)
|
||||||
)
|
)
|
||||||
@@ -39,7 +46,7 @@ func (h *HandlerV1) _handleList() {
|
|||||||
if files, err = os.ReadDir(h.cfg.ComDir); err != nil {
|
if files, err = os.ReadDir(h.cfg.ComDir); err != nil {
|
||||||
log.Error("Failed to read commands directory",
|
log.Error("Failed to read commands directory",
|
||||||
slog.String("error", err.Error()))
|
slog.String("error", err.Error()))
|
||||||
h.writeJSONError(http.StatusInternalServerError, "failed to read commands directory: "+err.Error())
|
utils.WriteJSONError(h.w, http.StatusInternalServerError, "failed to read commands directory: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Package sv1 provides the implementation of the Server V1 API handler.
|
||||||
|
// It includes utilities for handling API requests, extracting descriptions, and managing UUIDs.
|
||||||
package sv1
|
package sv1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,16 +10,7 @@ import (
|
|||||||
"github.com/akyaiy/GoSally-mvp/core/config"
|
"github.com/akyaiy/GoSally-mvp/core/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerV1UtilsContract interface {
|
// HandlerV1InitStruct structure is only for initialization
|
||||||
extractDescriptionStatic(path string) (string, error)
|
|
||||||
writeJSONError(status int, msg string)
|
|
||||||
newUUID() string
|
|
||||||
|
|
||||||
_errNotFound()
|
|
||||||
ErrNotFound(w http.ResponseWriter, r *http.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// structure only for initialization
|
|
||||||
type HandlerV1InitStruct struct {
|
type HandlerV1InitStruct struct {
|
||||||
Ver string
|
Ver string
|
||||||
Log slog.Logger
|
Log slog.Logger
|
||||||
@@ -26,6 +19,7 @@ type HandlerV1InitStruct struct {
|
|||||||
ListAllowedCmd *regexp.Regexp
|
ListAllowedCmd *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandlerV1 implements the ServerV1UtilsContract and serves as the main handler for API requests.
|
||||||
type HandlerV1 struct {
|
type HandlerV1 struct {
|
||||||
w http.ResponseWriter
|
w http.ResponseWriter
|
||||||
r *http.Request
|
r *http.Request
|
||||||
@@ -34,12 +28,16 @@ type HandlerV1 struct {
|
|||||||
|
|
||||||
cfg *config.ConfigConf
|
cfg *config.ConfigConf
|
||||||
|
|
||||||
|
// allowedCmd and listAllowedCmd are regular expressions used to validate command names.
|
||||||
allowedCmd *regexp.Regexp
|
allowedCmd *regexp.Regexp
|
||||||
listAllowedCmd *regexp.Regexp
|
listAllowedCmd *regexp.Regexp
|
||||||
|
|
||||||
ver string
|
ver string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitV1Server initializes a new HandlerV1 with the provided configuration and returns it.
|
||||||
|
// Should be carefull with giving to this function invalid parameters,
|
||||||
|
// because there is no validation of parameters in this function.
|
||||||
func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
||||||
return &HandlerV1{
|
return &HandlerV1{
|
||||||
log: o.Log,
|
log: o.Log,
|
||||||
@@ -50,18 +48,8 @@ func InitV1Server(o *HandlerV1InitStruct) *HandlerV1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) {
|
// GetVersion returns the API version of the HandlerV1, which is set during initialization.
|
||||||
h.w = w
|
// This version is used to identify the API version in the request routing.
|
||||||
h.r = r
|
|
||||||
h._handle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) {
|
|
||||||
h.w = w
|
|
||||||
h.r = r
|
|
||||||
h._handleList()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HandlerV1) GetVersion() string {
|
func (h *HandlerV1) GetVersion() string {
|
||||||
return h.ver
|
return h.ver
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,16 @@
|
|||||||
package sv1
|
package sv1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/akyaiy/GoSally-mvp/core/config"
|
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HandlerV1) ErrNotFound(w http.ResponseWriter, r *http.Request) {
|
func (h *HandlerV1) errNotFound(w http.ResponseWriter, r *http.Request) {
|
||||||
h.w = w
|
utils.WriteJSONError(h.w, http.StatusBadRequest, "invalid request")
|
||||||
h.r = r
|
|
||||||
h._errNotFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HandlerV1) newUUID() string {
|
|
||||||
bytes := make([]byte, int(config.GetInternalConsts().GetUUIDLength()/2))
|
|
||||||
_, err := rand.Read(bytes)
|
|
||||||
if err != nil {
|
|
||||||
h.log.Error("Failed to generate UUID", slog.String("error", err.Error()))
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HandlerV1) _errNotFound() {
|
|
||||||
h.writeJSONError(http.StatusBadRequest, "invalid request")
|
|
||||||
h.log.Error("HTTP request error",
|
h.log.Error("HTTP request error",
|
||||||
slog.String("remote", h.r.RemoteAddr),
|
slog.String("remote", h.r.RemoteAddr),
|
||||||
slog.String("method", h.r.Method),
|
slog.String("method", h.r.Method),
|
||||||
@@ -37,22 +18,6 @@ func (h *HandlerV1) _errNotFound() {
|
|||||||
slog.Int("status", http.StatusBadRequest))
|
slog.Int("status", http.StatusBadRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HandlerV1) writeJSONError(status int, msg string) {
|
|
||||||
h.w.Header().Set("Content-Type", "application/json")
|
|
||||||
h.w.WriteHeader(status)
|
|
||||||
resp := map[string]interface{}{
|
|
||||||
"status": "error",
|
|
||||||
"error": msg,
|
|
||||||
"code": status,
|
|
||||||
}
|
|
||||||
if err := json.NewEncoder(h.w).Encode(resp); err != nil {
|
|
||||||
h.log.Error("Failed to write JSON error response",
|
|
||||||
slog.String("error", err.Error()),
|
|
||||||
slog.Int("status", status))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HandlerV1) extractDescriptionStatic(path string) (string, error) {
|
func (h *HandlerV1) extractDescriptionStatic(path string) (string, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
22
core/utils/http_errors.go
Normal file
22
core/utils/http_errors.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// writeJSONError writes a JSON error response to the HTTP response writer.
|
||||||
|
// It sets the Content-Type to application/json, writes the specified HTTP status code
|
||||||
|
func WriteJSONError(w http.ResponseWriter, status int, msg string) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(status)
|
||||||
|
resp := map[string]any{
|
||||||
|
"status": "error",
|
||||||
|
"error": msg,
|
||||||
|
"code": status,
|
||||||
|
}
|
||||||
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
26
core/utils/internal_lua.go
Normal file
26
core/utils/internal_lua.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import lua "github.com/yuin/gopher-lua"
|
||||||
|
|
||||||
|
func ConvertLuaTypesToGolang(value lua.LValue) any {
|
||||||
|
switch value.Type() {
|
||||||
|
case lua.LTString:
|
||||||
|
return value.String()
|
||||||
|
case lua.LTNumber:
|
||||||
|
return float64(value.(lua.LNumber))
|
||||||
|
case lua.LTBool:
|
||||||
|
return bool(value.(lua.LBool))
|
||||||
|
case lua.LTTable:
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
if tbl, ok := value.(*lua.LTable); ok {
|
||||||
|
tbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||||||
|
result[key.String()] = ConvertLuaTypesToGolang(value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
case lua.LTNil:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return value.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
18
core/utils/uuid.go
Normal file
18
core/utils/uuid.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/akyaiy/GoSally-mvp/core/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewUUID() (string, error) {
|
||||||
|
bytes := make([]byte, int(config.GetInternalConsts().GetUUIDLength()/2))
|
||||||
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("failed to generate UUID: " + err.Error())
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(bytes), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user