From 84fa497169fa058a442a39f5a86da73fca51d4f6 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:26:23 +0800 Subject: [PATCH] fix(server): snapshot config with YAML to handle in-place mutations - Add oldConfigYaml to store previous config snapshot - Rebuild oldCfg from YAML in UpdateClients for reliable change detection - Initialize and refresh snapshot on startup and after updates - Prevents change detection bugs when Management API mutates cfg in place - Import gopkg.in/yaml.v3 --- internal/api/server.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/api/server.go b/internal/api/server.go index be3c05b0..c2e0e3c3 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -32,6 +32,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers/openai" "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" ) const oauthCallbackSuccessHTML = `Authentication successful

Authentication successful!

You can close this window.

This window will close automatically in 5 seconds.

` @@ -116,6 +117,10 @@ type Server struct { // cfg holds the current server configuration. cfg *config.Config + // oldConfigYaml stores a YAML snapshot of the previous configuration for change detection. + // This prevents issues when the config object is modified in place by Management API. + oldConfigYaml []byte + // accessManager handles request authentication providers. accessManager *sdkaccess.Manager @@ -220,6 +225,8 @@ func NewServer(cfg *config.Config, authManager *auth.Manager, accessManager *sdk currentPath: wd, envManagementSecret: envManagementSecret, } + // Save initial YAML snapshot + s.oldConfigYaml, _ = yaml.Marshal(cfg) s.applyAccessConfig(nil, cfg) // Initialize management handler s.mgmt = managementHandlers.NewHandler(cfg, configFilePath, authManager) @@ -654,7 +661,11 @@ func (s *Server) applyAccessConfig(oldCfg, newCfg *config.Config) { // - clients: The new slice of AI service clients // - cfg: The new application configuration func (s *Server) UpdateClients(cfg *config.Config) { - oldCfg := s.cfg + // Reconstruct old config from YAML snapshot to avoid reference sharing issues + var oldCfg *config.Config + if len(s.oldConfigYaml) > 0 { + _ = yaml.Unmarshal(s.oldConfigYaml, &oldCfg) + } // Update request logger enabled state if it has changed previousRequestLog := false @@ -735,6 +746,8 @@ func (s *Server) UpdateClients(cfg *config.Config) { s.applyAccessConfig(oldCfg, cfg) s.cfg = cfg + // Save YAML snapshot for next comparison + s.oldConfigYaml, _ = yaml.Marshal(cfg) s.handlers.UpdateClients(&cfg.SDKConfig) if !cfg.RemoteManagement.DisableControlPanel {