diff --git a/cmd/server/home_flag.go b/cmd/server/home_flag.go deleted file mode 100644 index ade94fbf3..000000000 --- a/cmd/server/home_flag.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "fmt" - "net" - "net/url" - "strconv" - "strings" - - "github.com/router-for-me/CLIProxyAPI/v7/internal/config" -) - -func parseHomeFlagConfig(rawAddr string, password string) (config.HomeConfig, error) { - rawAddr = strings.TrimSpace(rawAddr) - if rawAddr == "" { - return config.HomeConfig{}, fmt.Errorf("address is empty") - } - - if strings.Contains(rawAddr, "://") { - return parseHomeURLConfig(rawAddr, password) - } - - host, portStr, errSplit := net.SplitHostPort(rawAddr) - if errSplit != nil { - return config.HomeConfig{}, fmt.Errorf("expected host:port, redis://host:port, or rediss://host:port: %w", errSplit) - } - - host = strings.TrimSpace(host) - if host == "" { - return config.HomeConfig{}, fmt.Errorf("host is empty") - } - - port, errPort := parseHomePort(portStr) - if errPort != nil { - return config.HomeConfig{}, errPort - } - - return config.HomeConfig{ - Enabled: true, - Host: host, - Port: port, - Password: password, - }, nil -} - -func parseHomeURLConfig(rawAddr string, password string) (config.HomeConfig, error) { - parsed, errParse := url.Parse(rawAddr) - if errParse != nil { - return config.HomeConfig{}, fmt.Errorf("parse URL: %w", errParse) - } - - scheme := strings.ToLower(strings.TrimSpace(parsed.Scheme)) - if scheme != "redis" && scheme != "rediss" { - return config.HomeConfig{}, fmt.Errorf("unsupported URL scheme %q", parsed.Scheme) - } - - host := strings.TrimSpace(parsed.Hostname()) - if host == "" { - return config.HomeConfig{}, fmt.Errorf("host is empty") - } - - port, errPort := parseHomePort(parsed.Port()) - if errPort != nil { - return config.HomeConfig{}, errPort - } - - if password == "" && parsed.User != nil { - if urlPassword, ok := parsed.User.Password(); ok { - password = urlPassword - } - } - - homeCfg := config.HomeConfig{ - Enabled: true, - Host: host, - Port: port, - Password: password, - } - query := parsed.Query() - homeCfg.DisableClusterDiscovery = parseHomeBoolQuery(query, "disable-cluster-discovery", "disable_cluster_discovery") - - if scheme == "rediss" { - homeCfg.TLS.Enable = true - homeCfg.TLS.ServerName = strings.TrimSpace(firstHomeQueryValue(query, "server-name", "server_name")) - homeCfg.TLS.InsecureSkipVerify = parseHomeBoolQuery(query, "insecure-skip-verify", "insecure_skip_verify", "skip_verify") - homeCfg.TLS.CACert = strings.TrimSpace(firstHomeQueryValue(query, "ca-cert", "ca_cert")) - } - - return homeCfg, nil -} - -func parseHomePort(rawPort string) (int, error) { - rawPort = strings.TrimSpace(rawPort) - if rawPort == "" { - return 0, fmt.Errorf("port is empty") - } - - port, errPort := strconv.Atoi(rawPort) - if errPort != nil || port <= 0 || port > 65535 { - return 0, fmt.Errorf("invalid port %q", rawPort) - } - - return port, nil -} - -func firstHomeQueryValue(values url.Values, keys ...string) string { - for _, key := range keys { - if value := values.Get(key); value != "" { - return value - } - } - return "" -} - -func parseHomeBoolQuery(values url.Values, keys ...string) bool { - for _, key := range keys { - value := strings.TrimSpace(values.Get(key)) - if value == "" { - continue - } - parsed, errParse := strconv.ParseBool(value) - return errParse == nil && parsed - } - return false -} diff --git a/cmd/server/main.go b/cmd/server/main.go index 7da5b087a..1a5688eb9 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -10,9 +10,11 @@ import ( "fmt" "io" "io/fs" + "net" "net/url" "os" "path/filepath" + "strconv" "strings" "time" @@ -51,6 +53,120 @@ func init() { buildinfo.BuildDate = BuildDate } +func parseHomeFlagConfig(rawAddr string, password string) (config.HomeConfig, error) { + rawAddr = strings.TrimSpace(rawAddr) + if rawAddr == "" { + return config.HomeConfig{}, fmt.Errorf("address is empty") + } + + if strings.Contains(rawAddr, "://") { + return parseHomeURLConfig(rawAddr, password) + } + + host, portStr, errSplit := net.SplitHostPort(rawAddr) + if errSplit != nil { + return config.HomeConfig{}, fmt.Errorf("expected host:port, redis://host:port, or rediss://host:port: %w", errSplit) + } + + host = strings.TrimSpace(host) + if host == "" { + return config.HomeConfig{}, fmt.Errorf("host is empty") + } + + port, errPort := parseHomePort(portStr) + if errPort != nil { + return config.HomeConfig{}, errPort + } + + return config.HomeConfig{ + Enabled: true, + Host: host, + Port: port, + Password: password, + }, nil +} + +func parseHomeURLConfig(rawAddr string, password string) (config.HomeConfig, error) { + parsed, errParse := url.Parse(rawAddr) + if errParse != nil { + return config.HomeConfig{}, fmt.Errorf("parse URL: %w", errParse) + } + + scheme := strings.ToLower(strings.TrimSpace(parsed.Scheme)) + if scheme != "redis" && scheme != "rediss" { + return config.HomeConfig{}, fmt.Errorf("unsupported URL scheme %q", parsed.Scheme) + } + + host := strings.TrimSpace(parsed.Hostname()) + if host == "" { + return config.HomeConfig{}, fmt.Errorf("host is empty") + } + + port, errPort := parseHomePort(parsed.Port()) + if errPort != nil { + return config.HomeConfig{}, errPort + } + + if password == "" && parsed.User != nil { + if urlPassword, ok := parsed.User.Password(); ok { + password = urlPassword + } + } + + homeCfg := config.HomeConfig{ + Enabled: true, + Host: host, + Port: port, + Password: password, + } + query := parsed.Query() + homeCfg.DisableClusterDiscovery = parseHomeBoolQuery(query, "disable-cluster-discovery", "disable_cluster_discovery") + + if scheme == "rediss" { + homeCfg.TLS.Enable = true + homeCfg.TLS.ServerName = strings.TrimSpace(firstHomeQueryValue(query, "server-name", "server_name")) + homeCfg.TLS.InsecureSkipVerify = parseHomeBoolQuery(query, "insecure-skip-verify", "insecure_skip_verify", "skip_verify") + homeCfg.TLS.CACert = strings.TrimSpace(firstHomeQueryValue(query, "ca-cert", "ca_cert")) + } + + return homeCfg, nil +} + +func parseHomePort(rawPort string) (int, error) { + rawPort = strings.TrimSpace(rawPort) + if rawPort == "" { + return 0, fmt.Errorf("port is empty") + } + + port, errPort := strconv.Atoi(rawPort) + if errPort != nil || port <= 0 || port > 65535 { + return 0, fmt.Errorf("invalid port %q", rawPort) + } + + return port, nil +} + +func firstHomeQueryValue(values url.Values, keys ...string) string { + for _, key := range keys { + if value := values.Get(key); value != "" { + return value + } + } + return "" +} + +func parseHomeBoolQuery(values url.Values, keys ...string) bool { + for _, key := range keys { + value := strings.TrimSpace(values.Get(key)) + if value == "" { + continue + } + parsed, errParse := strconv.ParseBool(value) + return errParse == nil && parsed + } + return false +} + // main is the entry point of the application. // It parses command-line flags, loads configuration, and starts the appropriate // service based on the provided flags (login, codex-login, or server mode).