mirror of
https://gh-proxy.org/https://github.com/klinsmaya/CLIProxyAPI.git
synced 2026-11-08 01:16:47 +08:00
Use config snapshots for management reload
This commit is contained in:
@@ -28,7 +28,6 @@ import (
|
||||
geminiAuth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/gemini"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/kimi"
|
||||
xaiauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/xai"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
@@ -1267,13 +1266,11 @@ func (h *Handler) PatchAuthFileStatus(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "config api key entry not found"})
|
||||
return
|
||||
}
|
||||
if errSave := config.SaveConfigPreserveComments(h.configFilePath, h.cfg); errSave != nil {
|
||||
h.mu.Unlock()
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to save config: %v", errSave)})
|
||||
cfgSnapshot, okSnapshot := h.saveConfigAndSnapshotLocked(c)
|
||||
h.mu.Unlock()
|
||||
if !okSnapshot {
|
||||
return
|
||||
}
|
||||
cfgSnapshot := h.cfg
|
||||
h.mu.Unlock()
|
||||
h.reloadConfigAfterManagementSave(ctx, cfgSnapshot)
|
||||
if h.tokenStore != nil {
|
||||
_ = h.tokenStore.Delete(ctx, targetAuth.ID)
|
||||
|
||||
@@ -152,8 +152,29 @@ func (h *Handler) SetConfigReloadHook(hook func(context.Context, *config.Config)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
func (h *Handler) reloadConfigAfterManagementSave(ctx context.Context, cfg *config.Config) {
|
||||
if h == nil || cfg == nil {
|
||||
// snapshotConfigLocked clones the full runtime config while h.mu is held.
|
||||
// Callers must hold h.mu.
|
||||
func (h *Handler) snapshotConfigLocked() *config.Config {
|
||||
if h == nil || h.cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return h.cfg.CloneForRuntime()
|
||||
}
|
||||
|
||||
// saveConfigAndSnapshotLocked saves h.cfg and returns a full runtime config snapshot.
|
||||
// Callers must hold h.mu.
|
||||
func (h *Handler) saveConfigAndSnapshotLocked(c *gin.Context) (*config.Config, bool) {
|
||||
if errSave := config.SaveConfigPreserveComments(h.configFilePath, h.cfg); errSave != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to save config: %v", errSave)})
|
||||
return nil, false
|
||||
}
|
||||
return h.snapshotConfigLocked(), true
|
||||
}
|
||||
|
||||
// reloadConfigAfterManagementSave reloads from an independent config snapshot.
|
||||
// Callers must pass a full Config clone captured immediately after a successful save.
|
||||
func (h *Handler) reloadConfigAfterManagementSave(ctx context.Context, cfgSnapshot *config.Config) {
|
||||
if h == nil || cfgSnapshot == nil {
|
||||
return
|
||||
}
|
||||
h.mu.Lock()
|
||||
@@ -161,16 +182,18 @@ func (h *Handler) reloadConfigAfterManagementSave(ctx context.Context, cfg *conf
|
||||
host := h.pluginHost
|
||||
h.mu.Unlock()
|
||||
if hook != nil {
|
||||
hook(ctx, cfg)
|
||||
hook(ctx, cfgSnapshot)
|
||||
return
|
||||
}
|
||||
if host != nil {
|
||||
host.ApplyConfig(ctx, cfg)
|
||||
host.ApplyConfig(ctx, cfgSnapshot)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) reloadConfigAfterManagementSaveAsync(ctx context.Context, cfg *config.Config) {
|
||||
if h == nil || cfg == nil {
|
||||
// reloadConfigAfterManagementSaveAsync reloads from an independent config snapshot.
|
||||
// Callers must pass a full Config clone captured immediately after a successful save.
|
||||
func (h *Handler) reloadConfigAfterManagementSaveAsync(ctx context.Context, cfgSnapshot *config.Config) {
|
||||
if h == nil || cfgSnapshot == nil {
|
||||
return
|
||||
}
|
||||
reloadCtx := context.Background()
|
||||
@@ -183,7 +206,7 @@ func (h *Handler) reloadConfigAfterManagementSaveAsync(ctx context.Context, cfg
|
||||
log.WithField("panic", recovered).Error("management: async config reload panicked")
|
||||
}
|
||||
}()
|
||||
h.reloadConfigAfterManagementSave(reloadCtx, cfg)
|
||||
h.reloadConfigAfterManagementSave(reloadCtx, cfgSnapshot)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -226,9 +226,9 @@ func (h *Handler) installPluginFromStore(c *gin.Context, goos, goarch string) {
|
||||
if errInstall != nil {
|
||||
if unloadedBeforeWrite {
|
||||
h.mu.Lock()
|
||||
reloadCfg := h.cfg
|
||||
cfgSnapshot := h.snapshotConfigLocked()
|
||||
h.mu.Unlock()
|
||||
h.reloadConfigAfterManagementSave(c.Request.Context(), reloadCfg)
|
||||
h.reloadConfigAfterManagementSave(c.Request.Context(), cfgSnapshot)
|
||||
}
|
||||
if errors.Is(errInstall, pluginstore.ErrLoadedPluginLocked) {
|
||||
c.JSON(http.StatusConflict, gin.H{
|
||||
@@ -271,10 +271,10 @@ func (h *Handler) installPluginFromStore(c *gin.Context, goos, goarch string) {
|
||||
})
|
||||
return
|
||||
}
|
||||
reloadCfg := h.cfg
|
||||
cfgSnapshot := h.snapshotConfigLocked()
|
||||
h.mu.Unlock()
|
||||
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), reloadCfg)
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), cfgSnapshot)
|
||||
log.WithFields(log.Fields{
|
||||
"plugin_id": result.ID,
|
||||
"source_id": source.ID,
|
||||
|
||||
@@ -226,15 +226,13 @@ func (h *Handler) PatchPluginEnabled(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
h.cfg.Plugins.Configs[id] = updated
|
||||
if errSave := config.SaveConfigPreserveComments(h.configFilePath, h.cfg); errSave != nil {
|
||||
h.mu.Unlock()
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to save config: %v", errSave)})
|
||||
cfgSnapshot, okSnapshot := h.saveConfigAndSnapshotLocked(c)
|
||||
h.mu.Unlock()
|
||||
if !okSnapshot {
|
||||
return
|
||||
}
|
||||
reloadCfg := h.cfg
|
||||
h.mu.Unlock()
|
||||
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), reloadCfg)
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), cfgSnapshot)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
@@ -375,10 +373,10 @@ func (h *Handler) DeletePlugin(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
reloadCfg := h.cfg
|
||||
cfgSnapshot := h.snapshotConfigLocked()
|
||||
h.mu.Unlock()
|
||||
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), reloadCfg)
|
||||
h.reloadConfigAfterManagementSaveAsync(c.Request.Context(), cfgSnapshot)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "deleted",
|
||||
"id": htmlsanitize.String(id),
|
||||
|
||||
Reference in New Issue
Block a user