refresh back

This commit is contained in:
2025-10-03 16:55:09 +06:00
parent fbb530c619
commit ec2df9e612
7 changed files with 143 additions and 15 deletions

View File

@@ -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"})
}

View File

@@ -24,10 +24,13 @@ func setupKnockRoutes(api *gin.RouterGroup) {
ConfigYaml string `json:"config_yaml"` ConfigYaml string `json:"config_yaml"`
} }
if err := c.BindJSON(&req); err != nil { 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)}) c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)})
return return
} }
// fmt.Printf("req: %+v\n", req)
knocker := internal.NewPortKnocker() knocker := internal.NewPortKnocker()
// Определяем режим: inline или YAML // Определяем режим: inline или YAML
@@ -39,6 +42,8 @@ func setupKnockRoutes(api *gin.RouterGroup) {
return return
} }
fmt.Printf("config: %+v\n", config)
// Применяем дополнительные параметры из запроса // Применяем дополнительные параметры из запроса
if req.Gateway != "" { if req.Gateway != "" {
for i := range config.Targets { 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 { 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 return
} }
c.JSON(200, gin.H{"status": "ok"}) c.JSON(200, gin.H{"status": "ok"})
@@ -101,7 +107,6 @@ func parseInlineTargetsWithWait(targets, delay string, waitConnection bool, gate
host := strings.TrimSpace(parts[1]) host := strings.TrimSpace(parts[1])
portStr := strings.TrimSpace(parts[2]) portStr := strings.TrimSpace(parts[2])
if len(parts) == 4 { if len(parts) == 4 {
gateway = strings.TrimSpace(parts[3]) gateway = strings.TrimSpace(parts[3])
} }

View File

@@ -100,13 +100,17 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
// Разбираем формат [proto]:[host]:[port] // Разбираем формат [proto]:[host]:[port]
parts := strings.Split(targetStr, ":") parts := strings.Split(targetStr, ":")
if len(parts) != 3 { if len(parts) != 3 && len(parts) != 4 {
return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port]", targetStr) return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port] или [proto]:[host]:[port]:[gateway]", targetStr)
} }
protocol := strings.TrimSpace(parts[0]) protocol := strings.TrimSpace(parts[0])
host := strings.TrimSpace(parts[1]) host := strings.TrimSpace(parts[1])
portStr := strings.TrimSpace(parts[2]) portStr := strings.TrimSpace(parts[2])
gateway := ""
if len(parts) == 4 {
gateway = strings.TrimSpace(parts[3])
}
// Проверяем протокол // Проверяем протокол
if protocol != "tcp" && protocol != "udp" { if protocol != "tcp" && protocol != "udp" {
@@ -130,7 +134,7 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
Protocol: protocol, Protocol: protocol,
Delay: internal.Duration(delay), Delay: internal.Duration(delay),
WaitConnection: false, WaitConnection: false,
Gateway: "", Gateway: gateway,
} }
config.Targets = append(config.Targets, target) config.Targets = append(config.Targets, target)

View File

@@ -43,11 +43,16 @@ func runServe(cmd *cobra.Command, args []string) error {
port = "8888" port = "8888"
} }
host := os.Getenv("GO_KNOCKER_SERVE_HOST")
if strings.TrimSpace(port) == "" {
host = ""
}
r := gin.Default() r := gin.Default()
// CORS: разрешаем для локальной разработки // CORS: разрешаем для локальной разработки
r.Use(cors.New(cors.Config{ 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"}, AllowMethods: []string{"GET", "POST", "OPTIONS"},
AllowHeaders: []string{"Authorization", "Content-Type"}, AllowHeaders: []string{"Authorization", "Content-Type"},
AllowCredentials: true, AllowCredentials: true,
@@ -65,6 +70,6 @@ func runServe(cmd *cobra.Command, args []string) error {
setupCryptoRoutes(api, passHash) setupCryptoRoutes(api, passHash)
setupStaticRoutes(r, embeddedFS) setupStaticRoutes(r, embeddedFS)
fmt.Printf("Serving on :%s\n", port) fmt.Printf("Serving on %s:%s\n", host, port)
return r.Run(":" + port) return r.Run(host + ":" + port)
} }

View File

@@ -6,7 +6,6 @@ require (
github.com/gin-contrib/cors v1.7.2 github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
golang.org/x/sys v0.20.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@@ -36,6 +35,7 @@ require (
golang.org/x/arch v0.8.0 // indirect golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.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 golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect google.golang.org/protobuf v1.34.1 // indirect
) )

View File

@@ -7,7 +7,7 @@ import (
"port-knocker/cmd" "port-knocker/cmd"
) )
// Version и BuildTime устанавливаются при сборке через ldflags
var ( var (
Version = "v1.0.10" Version = "v1.0.10"
BuildTime = "unknown" BuildTime = "unknown"

View File

@@ -112,7 +112,6 @@ git push origin main
# Сборка бинарников # Сборка бинарников
log_info "Собираем бинарники для всех платформ..." log_info "Собираем бинарники для всех платформ..."
export VERSION_NUM="${VERSION#v}" export VERSION_NUM="${VERSION#v}"
# shellcheck disable=SC2155
export BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S') export BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
# Функция сборки для платформы # Функция сборки для платформы
@@ -156,7 +155,6 @@ log_info "Создаем Git тег..."
# Читаем release-notes.md и сохраняем содержимое в переменную NOTES # Читаем release-notes.md и сохраняем содержимое в переменную NOTES
NOTES=$(cat docs/scripts/release-notes.md) NOTES=$(cat docs/scripts/release-notes.md)
# Заменяем все переменные вида $VERSION в NOTES на их значения # Заменяем все переменные вида $VERSION в NOTES на их значения
# shellcheck disable=SC2001
NOTES=$(echo "$NOTES" | sed "s/\\\$VERSION/$VERSION/g") NOTES=$(echo "$NOTES" | sed "s/\\\$VERSION/$VERSION/g")
git tag -a "$VERSION" -m "$NOTES" git tag -a "$VERSION" -m "$NOTES"