mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
feat: implement management asset configuration and auto-updater
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/cmd"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/cmd"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/managementasset"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/store"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/store"
|
||||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator"
|
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator"
|
||||||
@@ -391,6 +392,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
cfg.AuthDir = resolvedAuthDir
|
cfg.AuthDir = resolvedAuthDir
|
||||||
}
|
}
|
||||||
|
managementasset.SetCurrentConfig(cfg)
|
||||||
|
|
||||||
// Create login options to be used in authentication flows.
|
// Create login options to be used in authentication flows.
|
||||||
options := &cmd.LoginOptions{
|
options := &cmd.LoginOptions{
|
||||||
@@ -434,6 +436,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Start the main proxy service
|
// Start the main proxy service
|
||||||
|
managementasset.StartAutoUpdater(context.Background(), configFilePath)
|
||||||
cmd.StartService(cfg, configFilePath, password)
|
cmd.StartService(cfg, configFilePath, password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ func NewServer(cfg *config.Config, authManager *auth.Manager, accessManager *sdk
|
|||||||
// Save initial YAML snapshot
|
// Save initial YAML snapshot
|
||||||
s.oldConfigYaml, _ = yaml.Marshal(cfg)
|
s.oldConfigYaml, _ = yaml.Marshal(cfg)
|
||||||
s.applyAccessConfig(nil, cfg)
|
s.applyAccessConfig(nil, cfg)
|
||||||
|
managementasset.SetCurrentConfig(cfg)
|
||||||
// Initialize management handler
|
// Initialize management handler
|
||||||
s.mgmt = managementHandlers.NewHandler(cfg, configFilePath, authManager)
|
s.mgmt = managementHandlers.NewHandler(cfg, configFilePath, authManager)
|
||||||
if optionState.localPassword != "" {
|
if optionState.localPassword != "" {
|
||||||
@@ -759,6 +760,7 @@ func (s *Server) UpdateClients(cfg *config.Config) {
|
|||||||
|
|
||||||
s.applyAccessConfig(oldCfg, cfg)
|
s.applyAccessConfig(oldCfg, cfg)
|
||||||
s.cfg = cfg
|
s.cfg = cfg
|
||||||
|
managementasset.SetCurrentConfig(cfg)
|
||||||
// Save YAML snapshot for next comparison
|
// Save YAML snapshot for next comparison
|
||||||
s.oldConfigYaml, _ = yaml.Marshal(cfg)
|
s.oldConfigYaml, _ = yaml.Marshal(cfg)
|
||||||
s.handlers.UpdateClients(&cfg.SDKConfig)
|
s.handlers.UpdateClients(&cfg.SDKConfig)
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -33,8 +35,83 @@ const ManagementFileName = managementAssetName
|
|||||||
var (
|
var (
|
||||||
lastUpdateCheckMu sync.Mutex
|
lastUpdateCheckMu sync.Mutex
|
||||||
lastUpdateCheckTime time.Time
|
lastUpdateCheckTime time.Time
|
||||||
|
|
||||||
|
currentConfigPtr atomic.Pointer[config.Config]
|
||||||
|
disableControlPanel atomic.Bool
|
||||||
|
schedulerOnce sync.Once
|
||||||
|
schedulerConfigPath atomic.Value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetCurrentConfig stores the latest configuration snapshot for management asset decisions.
|
||||||
|
func SetCurrentConfig(cfg *config.Config) {
|
||||||
|
if cfg == nil {
|
||||||
|
currentConfigPtr.Store(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prevDisabled := disableControlPanel.Load()
|
||||||
|
currentConfigPtr.Store(cfg)
|
||||||
|
disableControlPanel.Store(cfg.RemoteManagement.DisableControlPanel)
|
||||||
|
|
||||||
|
if prevDisabled && !cfg.RemoteManagement.DisableControlPanel {
|
||||||
|
lastUpdateCheckMu.Lock()
|
||||||
|
lastUpdateCheckTime = time.Time{}
|
||||||
|
lastUpdateCheckMu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartAutoUpdater launches a background goroutine that periodically ensures the management asset is up to date.
|
||||||
|
// It respects the disable-control-panel flag on every iteration and supports hot-reloaded configurations.
|
||||||
|
func StartAutoUpdater(ctx context.Context, configFilePath string) {
|
||||||
|
configFilePath = strings.TrimSpace(configFilePath)
|
||||||
|
if configFilePath == "" {
|
||||||
|
log.Debug("management asset auto-updater skipped: empty config path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerConfigPath.Store(configFilePath)
|
||||||
|
|
||||||
|
schedulerOnce.Do(func() {
|
||||||
|
go runAutoUpdater(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAutoUpdater(ctx context.Context) {
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(updateCheckInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
runOnce := func() {
|
||||||
|
cfg := currentConfigPtr.Load()
|
||||||
|
if cfg == nil {
|
||||||
|
log.Debug("management asset auto-updater skipped: config not yet available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if disableControlPanel.Load() {
|
||||||
|
log.Debug("management asset auto-updater skipped: control panel disabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath, _ := schedulerConfigPath.Load().(string)
|
||||||
|
staticDir := StaticDir(configPath)
|
||||||
|
EnsureLatestManagementHTML(ctx, staticDir, cfg.ProxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
runOnce()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
runOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newHTTPClient(proxyURL string) *http.Client {
|
func newHTTPClient(proxyURL string) *http.Client {
|
||||||
client := &http.Client{Timeout: 15 * time.Second}
|
client := &http.Client{Timeout: 15 * time.Second}
|
||||||
|
|
||||||
@@ -109,6 +186,11 @@ func EnsureLatestManagementHTML(ctx context.Context, staticDir string, proxyURL
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if disableControlPanel.Load() {
|
||||||
|
log.Debug("management asset sync skipped: control panel disabled by configuration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
staticDir = strings.TrimSpace(staticDir)
|
staticDir = strings.TrimSpace(staticDir)
|
||||||
if staticDir == "" {
|
if staticDir == "" {
|
||||||
log.Debug("management asset sync skipped: empty static directory")
|
log.Debug("management asset sync skipped: empty static directory")
|
||||||
|
|||||||
Reference in New Issue
Block a user