From fe5637a6bb1b9d3d96d200e616558eedf4b38b7c Mon Sep 17 00:00:00 2001 From: qxo <49526356@qq.com> Date: Sat, 11 Apr 2026 00:36:06 +0800 Subject: [PATCH] feat: add -port CLI flag to lock port configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 -port 命令行参数强制指定端口 - CLI 指定的端口会被锁定,无法通过 API 修改 - GET /api/config/port 返回 portLocked 状态 - PUT /api/config/port 在锁定时返回 403 禁止访问 --- cmd/server/main.go | 13 ++++++++++++- cmd/server/webui/api/config.go | 8 +++++++- internal/config/config.go | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 34de96f2..7480d9fa 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "encoding/hex" "errors" + "flag" "net/http" "os" "os/signal" @@ -18,6 +19,9 @@ import ( ) func main() { + // Parse command line flags + portFlag := flag.Int("port", 0, "Force specific port (locked, cannot be changed via API)") + flag.Parse() dataDir := resolveDataDir() if err := os.MkdirAll(dataDir, 0755); err != nil { logger.Error("Failed to create data dir %s: %v", dataDir, err) @@ -42,6 +46,13 @@ func main() { os.Exit(1) } + // Handle -port CLI flag (overrides config and locks port) + if *portFlag > 0 { + cfg.Port = *portFlag + cfg.LockPort() + logger.Info("Port locked to %d via CLI flag", *portFlag) + } + if cfg.BasicAuthEnabled && cfg.BasicAuthPassword == "" { randomPassword := generateRandomPassword(16) cfg.BasicAuthPassword = randomPassword @@ -192,4 +203,4 @@ func generateRandomPassword(length int) string { return string(fallback) } return hex.EncodeToString(bytes)[:length] -} \ No newline at end of file +} diff --git a/cmd/server/webui/api/config.go b/cmd/server/webui/api/config.go index 44b84bd5..681116b8 100644 --- a/cmd/server/webui/api/config.go +++ b/cmd/server/webui/api/config.go @@ -146,9 +146,15 @@ func (h *Handler) handleConfigPort(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: WriteSuccess(w, map[string]interface{}{ - "port": h.config.GetPort(), + "port": h.config.GetPort(), + "portLocked": h.config.IsPortLocked(), }) case http.MethodPut: + if h.config.IsPortLocked() { + WriteError(w, http.StatusForbidden, "Port is locked by CLI flag and cannot be changed") + return + } + var req struct { Port int `json:"port"` } diff --git a/internal/config/config.go b/internal/config/config.go index 2ca3c28d..4d5999b5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -160,6 +160,7 @@ type ProxyConfig struct { // Config represents the application configuration type Config struct { Port int `json:"port"` + PortLocked bool `json:"-"` // CLI forced port, cannot be changed via API BasicAuthEnabled bool `json:"basicAuthEnabled"` BasicAuthUsername string `json:"basicAuthUsername"` BasicAuthPassword string `json:"basicAuthPassword"` @@ -318,12 +319,30 @@ func (c *Config) UpdateEndpoints(endpoints []Endpoint) { } // UpdatePort updates the port (thread-safe) +// If PortLocked is true, the port cannot be changed func (c *Config) UpdatePort(port int) { c.mu.Lock() defer c.mu.Unlock() + if c.PortLocked { + return + } c.Port = port } +// LockPort locks the port so it cannot be changed via API +func (c *Config) LockPort() { + c.mu.Lock() + defer c.mu.Unlock() + c.PortLocked = true +} + +// IsPortLocked returns true if the port is locked +func (c *Config) IsPortLocked() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.PortLocked +} + // UpdateLogLevel updates the log level (thread-safe) func (c *Config) UpdateLogLevel(level int) { c.mu.Lock()