From ec2df9e6125a4b31727ac06c12d72859ff1ef7bb Mon Sep 17 00:00:00 2001 From: Anton Kuznetcov Date: Fri, 3 Oct 2025 16:55:09 +0600 Subject: [PATCH] refresh back --- back/cmd/knock-local/main.go | 116 ++++++++++++++++++++++++++++++++++ back/cmd/knock_routes.go | 13 ++-- back/cmd/root.go | 12 ++-- back/cmd/serve.go | 11 +++- back/go.mod | 2 +- back/main.go | 2 +- back/scripts/quick-release.sh | 2 - 7 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 back/cmd/knock-local/main.go diff --git a/back/cmd/knock-local/main.go b/back/cmd/knock-local/main.go new file mode 100644 index 0000000..0f66bfb --- /dev/null +++ b/back/cmd/knock-local/main.go @@ -0,0 +1,116 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "time" + + "port-knocker/internal" +) + +type Request struct { + Targets []string `json:"targets"` + Delay string `json:"delay"` + Verbose bool `json:"verbose"` + Gateway string `json:"gateway"` +} + +type Response struct { + Success bool `json:"success"` + Error string `json:"error,omitempty"` + Message string `json:"message,omitempty"` +} + +func parseTargets(in []string, delayStr string, gateway string) (*internal.Config, error) { + cfg := &internal.Config{Targets: []internal.Target{}} + d := time.Second + if strings.TrimSpace(delayStr) != "" { + dur, err := time.ParseDuration(delayStr) + if err == nil { + d = dur + } + } + for _, t := range in { + t = strings.TrimSpace(t) + if t == "" { + continue + } + parts := strings.Split(t, ":") + if len(parts) < 3 { + return nil, fmt.Errorf("invalid target: %s", t) + } + protocol := strings.ToLower(parts[0]) + host := parts[1] + portStr := parts[2] + gw := "" + if len(parts) >= 4 && strings.TrimSpace(parts[3]) != "" { + gw = strings.TrimSpace(parts[3]) + } else if strings.TrimSpace(gateway) != "" { + gw = strings.TrimSpace(gateway) + } + // single port + var port int + fmt.Sscanf(portStr, "%d", &port) + cfg.Targets = append(cfg.Targets, internal.Target{ + Host: host, + Ports: []int{port}, + Protocol: protocol, + Delay: internal.Duration(d), + WaitConnection: false, + Gateway: gw, + }) + } + return cfg, nil +} + +func readAllStdin() ([]byte, error) { + reader := bufio.NewReader(os.Stdin) + var b []byte + for { + chunk, isPrefix, err := reader.ReadLine() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + b = append(b, chunk...) + if !isPrefix { + b = append(b, '\n') + } + } + return b, nil +} + +func main() { + // Read JSON from stdin + data, err := readAllStdin() + if err != nil || len(strings.TrimSpace(string(data))) == 0 { + // fallback to args? not required; expect stdin + } + var req Request + if err := json.Unmarshal(data, &req); err != nil { + _ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: fmt.Sprintf("invalid json: %v", err)}) + return + } + + cfg, err := parseTargets(req.Targets, req.Delay, req.Gateway) + if err != nil { + _ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: err.Error()}) + return + } + + pk := internal.NewPortKnocker() + // Всегда без лишних логов на stdout (stdout должен содержать только JSON ответ) + verbose := false + if err := pk.ExecuteWithConfig(cfg, verbose, false); err != nil { + _ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: err.Error()}) + return + } + + _ = json.NewEncoder(os.Stdout).Encode(Response{Success: true, Message: "ok"}) +} diff --git a/back/cmd/knock_routes.go b/back/cmd/knock_routes.go index 6596def..f0c3a16 100644 --- a/back/cmd/knock_routes.go +++ b/back/cmd/knock_routes.go @@ -24,10 +24,13 @@ func setupKnockRoutes(api *gin.RouterGroup) { ConfigYaml string `json:"config_yaml"` } if err := c.BindJSON(&req); err != nil { + // fmt.Printf("bad json: %v\n", err) c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)}) return } + // fmt.Printf("req: %+v\n", req) + knocker := internal.NewPortKnocker() // Определяем режим: inline или YAML @@ -39,6 +42,8 @@ func setupKnockRoutes(api *gin.RouterGroup) { return } + fmt.Printf("config: %+v\n", config) + // Применяем дополнительные параметры из запроса if req.Gateway != "" { for i := range config.Targets { @@ -47,7 +52,8 @@ func setupKnockRoutes(api *gin.RouterGroup) { } if err := knocker.ExecuteWithConfig(config, req.Verbose, req.WaitConnection); err != nil { - c.JSON(400, gin.H{"error": err.Error()}) + fmt.Printf("error: %v\n", err) + c.JSON(400, gin.H{"status": "error","error": err.Error()}) return } c.JSON(200, gin.H{"status": "ok"}) @@ -71,7 +77,7 @@ func setupKnockRoutes(api *gin.RouterGroup) { } } - if err := knocker.ExecuteWithConfig(&config, true || req.Verbose, req.WaitConnection); err != nil { + if err := knocker.ExecuteWithConfig(&config, true || req.Verbose, req.WaitConnection); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } @@ -100,8 +106,7 @@ func parseInlineTargetsWithWait(targets, delay string, waitConnection bool, gate protocol := strings.TrimSpace(parts[0]) host := strings.TrimSpace(parts[1]) portStr := strings.TrimSpace(parts[2]) - - + if len(parts) == 4 { gateway = strings.TrimSpace(parts[3]) } diff --git a/back/cmd/root.go b/back/cmd/root.go index 59b9202..e73a84e 100644 --- a/back/cmd/root.go +++ b/back/cmd/root.go @@ -1,6 +1,6 @@ package cmd -import ( +import ( "fmt" "strconv" "strings" @@ -100,13 +100,17 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) { // Разбираем формат [proto]:[host]:[port] parts := strings.Split(targetStr, ":") - if len(parts) != 3 { - return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port]", targetStr) + if len(parts) != 3 && len(parts) != 4 { + return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port] или [proto]:[host]:[port]:[gateway]", targetStr) } protocol := strings.TrimSpace(parts[0]) host := strings.TrimSpace(parts[1]) portStr := strings.TrimSpace(parts[2]) + gateway := "" + if len(parts) == 4 { + gateway = strings.TrimSpace(parts[3]) + } // Проверяем протокол if protocol != "tcp" && protocol != "udp" { @@ -130,7 +134,7 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) { Protocol: protocol, Delay: internal.Duration(delay), WaitConnection: false, - Gateway: "", + Gateway: gateway, } config.Targets = append(config.Targets, target) diff --git a/back/cmd/serve.go b/back/cmd/serve.go index 454bf64..deea27b 100644 --- a/back/cmd/serve.go +++ b/back/cmd/serve.go @@ -43,11 +43,16 @@ func runServe(cmd *cobra.Command, args []string) error { port = "8888" } + host := os.Getenv("GO_KNOCKER_SERVE_HOST") + if strings.TrimSpace(port) == "" { + host = "" + } + r := gin.Default() // CORS: разрешаем для локальной разработки r.Use(cors.New(cors.Config{ - AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:8888"}, + AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:" + port}, AllowMethods: []string{"GET", "POST", "OPTIONS"}, AllowHeaders: []string{"Authorization", "Content-Type"}, AllowCredentials: true, @@ -65,6 +70,6 @@ func runServe(cmd *cobra.Command, args []string) error { setupCryptoRoutes(api, passHash) setupStaticRoutes(r, embeddedFS) - fmt.Printf("Serving on :%s\n", port) - return r.Run(":" + port) + fmt.Printf("Serving on %s:%s\n", host, port) + return r.Run(host + ":" + port) } diff --git a/back/go.mod b/back/go.mod index c4a9a82..eb83c7e 100644 --- a/back/go.mod +++ b/back/go.mod @@ -6,7 +6,6 @@ require ( github.com/gin-contrib/cors v1.7.2 github.com/gin-gonic/gin v1.10.0 github.com/spf13/cobra v1.8.0 - golang.org/x/sys v0.20.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -36,6 +35,7 @@ require ( golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 golang.org/x/text v0.15.0 // indirect google.golang.org/protobuf v1.34.1 // indirect ) diff --git a/back/main.go b/back/main.go index d06f9fe..845fa06 100644 --- a/back/main.go +++ b/back/main.go @@ -7,7 +7,7 @@ import ( "port-knocker/cmd" ) - +// Version и BuildTime устанавливаются при сборке через ldflags var ( Version = "v1.0.10" BuildTime = "unknown" diff --git a/back/scripts/quick-release.sh b/back/scripts/quick-release.sh index 0cb3133..8024937 100755 --- a/back/scripts/quick-release.sh +++ b/back/scripts/quick-release.sh @@ -112,7 +112,6 @@ git push origin main # Сборка бинарников log_info "Собираем бинарники для всех платформ..." export VERSION_NUM="${VERSION#v}" -# shellcheck disable=SC2155 export BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S') # Функция сборки для платформы @@ -156,7 +155,6 @@ log_info "Создаем Git тег..." # Читаем release-notes.md и сохраняем содержимое в переменную NOTES NOTES=$(cat docs/scripts/release-notes.md) # Заменяем все переменные вида $VERSION в NOTES на их значения -# shellcheck disable=SC2001 NOTES=$(echo "$NOTES" | sed "s/\\\$VERSION/$VERSION/g") git tag -a "$VERSION" -m "$NOTES"