From 0aa8706ef7632fcbc9f65cda5f81bcf8bcb3e43f Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Mon, 13 Oct 2025 09:16:38 +0800 Subject: [PATCH] feat(config): Treat empty BaseURL for Codex keys as deletion --- .../api/handlers/management/config_lists.go | 55 +++++++++++++++---- internal/config/config.go | 21 +++++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/internal/api/handlers/management/config_lists.go b/internal/api/handlers/management/config_lists.go index b49940b8..fe8d3141 100644 --- a/internal/api/handlers/management/config_lists.go +++ b/internal/api/handlers/management/config_lists.go @@ -335,7 +335,17 @@ func (h *Handler) PutCodexKeys(c *gin.Context) { } arr = obj.Items } - h.cfg.CodexKey = arr + // Filter out codex entries with empty base-url (treat as removed) + filtered := make([]config.CodexKey, 0, len(arr)) + for i := range arr { + entry := arr[i] + entry.BaseURL = strings.TrimSpace(entry.BaseURL) + if entry.BaseURL == "" { + continue + } + filtered = append(filtered, entry) + } + h.cfg.CodexKey = filtered h.persist(c) } func (h *Handler) PatchCodexKey(c *gin.Context) { @@ -348,19 +358,44 @@ func (h *Handler) PatchCodexKey(c *gin.Context) { c.JSON(400, gin.H{"error": "invalid body"}) return } - if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.CodexKey) { - h.cfg.CodexKey[*body.Index] = *body.Value - h.persist(c) - return - } - if body.Match != nil { - for i := range h.cfg.CodexKey { - if h.cfg.CodexKey[i].APIKey == *body.Match { - h.cfg.CodexKey[i] = *body.Value + // If base-url becomes empty, delete instead of update + if strings.TrimSpace(body.Value.BaseURL) == "" { + if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.CodexKey) { + h.cfg.CodexKey = append(h.cfg.CodexKey[:*body.Index], h.cfg.CodexKey[*body.Index+1:]...) + h.persist(c) + return + } + if body.Match != nil { + out := make([]config.CodexKey, 0, len(h.cfg.CodexKey)) + removed := false + for i := range h.cfg.CodexKey { + if !removed && h.cfg.CodexKey[i].APIKey == *body.Match { + removed = true + continue + } + out = append(out, h.cfg.CodexKey[i]) + } + if removed { + h.cfg.CodexKey = out h.persist(c) return } } + } else { + if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.CodexKey) { + h.cfg.CodexKey[*body.Index] = *body.Value + h.persist(c) + return + } + if body.Match != nil { + for i := range h.cfg.CodexKey { + if h.cfg.CodexKey[i].APIKey == *body.Match { + h.cfg.CodexKey[i] = *body.Value + h.persist(c) + return + } + } + } } c.JSON(404, gin.H{"error": "item not found"}) } diff --git a/internal/config/config.go b/internal/config/config.go index e144c263..50dcd5a5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -211,6 +211,9 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) { // Sanitize OpenAI compatibility providers: drop entries without base-url sanitizeOpenAICompatibility(&cfg) + // Sanitize Codex keys: drop entries without base-url + sanitizeCodexKeys(&cfg) + // Return the populated configuration struct. return &cfg, nil } @@ -236,6 +239,24 @@ func sanitizeOpenAICompatibility(cfg *Config) { cfg.OpenAICompatibility = out } +// sanitizeCodexKeys removes Codex API key entries missing a BaseURL. +// It trims whitespace and preserves order for remaining entries. +func sanitizeCodexKeys(cfg *Config) { + if cfg == nil || len(cfg.CodexKey) == 0 { + return + } + out := make([]CodexKey, 0, len(cfg.CodexKey)) + for i := range cfg.CodexKey { + e := cfg.CodexKey[i] + e.BaseURL = strings.TrimSpace(e.BaseURL) + if e.BaseURL == "" { + continue + } + out = append(out, e) + } + cfg.CodexKey = out +} + func syncInlineAccessProvider(cfg *Config) { if cfg == nil { return