Implement TLS support: update configuration for TLS, modify server to handle HTTPS, and enhance logging for request handling

This commit is contained in:
alex
2025-06-24 22:15:37 +03:00
parent 973c060e5f
commit ef1efdd585
8 changed files with 75 additions and 54 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
bak/ bak/
bin/ bin/
cert/

View File

@@ -1,7 +1,7 @@
APP_NAME := node APP_NAME := node
BIN_DIR := bin BIN_DIR := bin
GOPATH := $(shell go env GOPATH) GOPATH := $(shell go env GOPATH)
.PHONY: all build run test fmt vet lint check clean .PHONY: all build run runq test fmt vet lint check clean
all: build all: build
@@ -25,6 +25,9 @@ build:
run: build run: build
./$(BIN_DIR)/$(APP_NAME) ./$(BIN_DIR)/$(APP_NAME)
runq: build
./$(BIN_DIR)/$(APP_NAME) | jq
test: test:
@go test ./... | grep -v '^?' || true @go test ./... | grep -v '^?' || true

View File

@@ -10,4 +10,9 @@ http_server:
- b1 - b1
- s2 - s2
tls:
enabled: "true"
cert_file: "./cert/server.crt"
key_file: "./cert/server.key"
com_dir: "com/" com_dir: "com/"

View File

@@ -39,11 +39,21 @@ func main() {
Config: cfg, Config: cfg,
}, serverv1) }, serverv1)
r := chi.NewRouter() r := chi.NewRouter()
r.Route("/{ver}/com", func(r chi.Router) { r.Route("/api/{ver}/com", func(r chi.Router) {
r.Get("/", s.HandleList) r.Get("/", s.HandleList)
r.Get("/{cmd}", s.Handle) r.Get("/{cmd}", s.Handle)
}) })
r.NotFound(serverv1.ErrNotFound) r.NotFound(serverv1.ErrNotFound)
log.Info("Server started", slog.String("address", cfg.Address)) if cfg.TlsEnabled == "true" {
http.ListenAndServe(cfg.Address, r) log.Info("Server started with TLS", slog.String("address", cfg.Address))
err := http.ListenAndServeTLS(cfg.Address, cfg.CertFile, cfg.KeyFile, r)
if err != nil {
log.Error("Failed to start HTTPS server", slog.String("error", err.Error()))
}
}
log.Info("Server started", slog.String("address", cfg.Address))
err := http.ListenAndServe(cfg.Address, r)
if err != nil {
log.Error("Failed to start HTTP server", slog.String("error", err.Error()))
}
} }

View File

@@ -1,6 +1,6 @@
--- #description = "Echoes back the message provided in the 'msg' parameter." --- #description = "Echoes back the message."
--- #args
local mod = require("_for_echo") --- msg = the message
if not Params.msg then if not Params.msg then
Result.status = "error" Result.status = "error"
@@ -9,5 +9,5 @@ if not Params.msg then
end end
Result.status = "ok" Result.status = "ok"
Result.answer = mod.translate(Params.msg) Result.answer = Params.msg
return return

View File

@@ -12,6 +12,13 @@ type ConfigConf struct {
Mode string `yaml:"mode" env-default:"dev"` Mode string `yaml:"mode" env-default:"dev"`
ComDir string `yaml:"com_dir" env-default:"./com/"` ComDir string `yaml:"com_dir" env-default:"./com/"`
HTTPServer `yaml:"http_server"` HTTPServer `yaml:"http_server"`
TLS `yaml:"tls"`
}
type TLS struct {
TlsEnabled string `yaml:"enabled" env-default:"false"`
CertFile string `yaml:"cert_file" env-default:"./cert/server.crt"`
KeyFile string `yaml:"key_file" env-default:"./cert/server.key"`
} }
type HTTPServer struct { type HTTPServer struct {
@@ -31,6 +38,7 @@ type ConfigEnv struct {
} }
func MustLoadConfig() *ConfigConf { func MustLoadConfig() *ConfigConf {
log.SetOutput(os.Stderr)
var configEnv ConfigEnv var configEnv ConfigEnv
if err := cleanenv.ReadEnv(&configEnv); err != nil { if err := cleanenv.ReadEnv(&configEnv); err != nil {
log.Fatalf("Failed to read environment variables: %v", err) log.Fatalf("Failed to read environment variables: %v", err)

View File

@@ -13,18 +13,23 @@ import (
func (h *HandlerV1) _handle() { func (h *HandlerV1) _handle() {
uuid16 := h.newUUID() uuid16 := h.newUUID()
h.log.Info("Received request", log := h.log.With(
slog.Group("request",
slog.String("version", "v1"), slog.String("version", "v1"),
slog.String("url", h.r.URL.String()),
slog.String("method", h.r.Method),
),
slog.Group("connection",
slog.String("connection-uuid", uuid16), slog.String("connection-uuid", uuid16),
slog.String("remote", h.r.RemoteAddr), slog.String("remote", h.r.RemoteAddr),
slog.String("method", h.r.Method), ),
slog.String("url", h.r.URL.String())) )
log.Info("Received request")
cmd := chi.URLParam(h.r, "cmd") cmd := chi.URLParam(h.r, "cmd")
var scriptPath string 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", log.Error("HTTP request error",
slog.String("connection-uuid", uuid16),
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))
@@ -32,8 +37,7 @@ func (h *HandlerV1) _handle() {
return return
} }
if !h.listAllowedCmd.MatchString(cmd) { if !h.listAllowedCmd.MatchString(cmd) {
h.log.Error("HTTP request error", log.Error("HTTP request error",
slog.String("connection-uuid", uuid16),
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))
@@ -41,8 +45,7 @@ func (h *HandlerV1) _handle() {
return return
} }
if scriptPath = h.comMatch(chi.URLParam(h.r, "ver"), cmd); scriptPath == "" { if scriptPath = h.comMatch(chi.URLParam(h.r, "ver"), cmd); scriptPath == "" {
h.log.Error("HTTP request error", log.Error("HTTP request error",
slog.String("connection-uuid", uuid16),
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))
@@ -52,8 +55,7 @@ func (h *HandlerV1) _handle() {
scriptPath = filepath.Join(h.cfg.ComDir, scriptPath) scriptPath = filepath.Join(h.cfg.ComDir, scriptPath)
if _, err := os.Stat(scriptPath); err != nil { if _, err := os.Stat(scriptPath); err != nil {
h.log.Error("HTTP request error", log.Error("HTTP request error",
slog.String("connection-uuid", uuid16),
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))
@@ -79,19 +81,18 @@ func (h *HandlerV1) _handle() {
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 {
h.log.Error("Failed to prepare lua environment", log.Error("Failed to prepare lua environment",
slog.String("connection-uuid", uuid16),
slog.String("error", err.Error())) slog.String("error", err.Error()))
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
return return
} }
} else { } else {
h.log.Error("No environment preparation script found, skipping preparation", slog.String("connection-uuid", uuid16), slog.String("error", err.Error())) log.Error("No environment preparation script found, skipping preparation",
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", log.Error("Failed to execute lua script",
slog.String("connection-uuid", uuid16),
slog.String("error", err.Error())) slog.String("error", err.Error()))
h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error()) h.writeJSONError(http.StatusInternalServerError, "lua error: "+err.Error())
return return
@@ -117,23 +118,16 @@ 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", log.Info("Command executed with error",
slog.String("connection-uuid", uuid16),
slog.String("cmd", cmd), slog.String("cmd", cmd),
slog.Any("result", out)) slog.Any("result", out))
case "ok": case "ok":
h.log.Info("Command executed successfully", log.Info("Command executed successfully",
slog.String("connection-uuid", uuid16),
slog.String("cmd", cmd), slog.Any("result", out)) slog.String("cmd", cmd), slog.Any("result", out))
default: default:
h.log.Info("Command executed and returned an unknown status", log.Info("Command executed and returned an unknown status",
slog.String("connection-uuid", uuid16),
slog.String("cmd", cmd), slog.String("cmd", cmd),
slog.Any("result", out)) slog.Any("result", out))
} }
h.log.Info("Session completed", 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()))
} }

View File

@@ -13,17 +13,22 @@ import (
func (h *HandlerV1) _handleList() { func (h *HandlerV1) _handleList() {
uuid16 := h.newUUID() uuid16 := h.newUUID()
h.log.Info("Received request", log := h.log.With(
slog.Group("request",
slog.String("version", "v1"), slog.String("version", "v1"),
slog.String("url", h.r.URL.String()),
slog.String("method", h.r.Method),
),
slog.Group("connection",
slog.String("connection-uuid", uuid16), slog.String("connection-uuid", uuid16),
slog.String("remote", h.r.RemoteAddr), slog.String("remote", h.r.RemoteAddr),
slog.String("method", h.r.Method), ),
slog.String("url", h.r.URL.String())) )
log.Info("Received request")
type ComMeta struct { type ComMeta struct {
Description string Description string `json:"Description"`
Arguments map[string]string `json:"Arguments,omitempty"`
} }
var ( var (
files []os.DirEntry files []os.DirEntry
err error err error
@@ -32,7 +37,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 {
h.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()) h.writeJSONError(http.StatusInternalServerError, "failed to read commands directory: "+err.Error())
return return
@@ -94,14 +99,9 @@ func (h *HandlerV1) _handleList() {
} }
} }
h.log.Info("Command list prepared", log.Debug("Command list prepared")
slog.String("connection-uuid", uuid16))
h.log.Info("Session completed", 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") h.w.Header().Set("Content-Type", "application/json")
json.NewEncoder(h.w).Encode(commands) json.NewEncoder(h.w).Encode(commands)