@@ -13,8 +13,10 @@ import (
1313 "path/filepath"
1414 "strings"
1515 "sync"
16+ "sync/atomic"
1617 "time"
1718
19+ "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
1820 "github.com/router-for-me/CLIProxyAPI/v6/internal/util"
1921 sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
2022 log "github.com/sirupsen/logrus"
@@ -33,8 +35,83 @@ const ManagementFileName = managementAssetName
3335var (
3436 lastUpdateCheckMu sync.Mutex
3537 lastUpdateCheckTime time.Time
38+
39+ currentConfigPtr atomic.Pointer [config.Config ]
40+ disableControlPanel atomic.Bool
41+ schedulerOnce sync.Once
42+ schedulerConfigPath atomic.Value
3643)
3744
45+ // SetCurrentConfig stores the latest configuration snapshot for management asset decisions.
46+ func SetCurrentConfig (cfg * config.Config ) {
47+ if cfg == nil {
48+ currentConfigPtr .Store (nil )
49+ return
50+ }
51+
52+ prevDisabled := disableControlPanel .Load ()
53+ currentConfigPtr .Store (cfg )
54+ disableControlPanel .Store (cfg .RemoteManagement .DisableControlPanel )
55+
56+ if prevDisabled && ! cfg .RemoteManagement .DisableControlPanel {
57+ lastUpdateCheckMu .Lock ()
58+ lastUpdateCheckTime = time.Time {}
59+ lastUpdateCheckMu .Unlock ()
60+ }
61+ }
62+
63+ // StartAutoUpdater launches a background goroutine that periodically ensures the management asset is up to date.
64+ // It respects the disable-control-panel flag on every iteration and supports hot-reloaded configurations.
65+ func StartAutoUpdater (ctx context.Context , configFilePath string ) {
66+ configFilePath = strings .TrimSpace (configFilePath )
67+ if configFilePath == "" {
68+ log .Debug ("management asset auto-updater skipped: empty config path" )
69+ return
70+ }
71+
72+ schedulerConfigPath .Store (configFilePath )
73+
74+ schedulerOnce .Do (func () {
75+ go runAutoUpdater (ctx )
76+ })
77+ }
78+
79+ func runAutoUpdater (ctx context.Context ) {
80+ if ctx == nil {
81+ ctx = context .Background ()
82+ }
83+
84+ ticker := time .NewTicker (updateCheckInterval )
85+ defer ticker .Stop ()
86+
87+ runOnce := func () {
88+ cfg := currentConfigPtr .Load ()
89+ if cfg == nil {
90+ log .Debug ("management asset auto-updater skipped: config not yet available" )
91+ return
92+ }
93+ if disableControlPanel .Load () {
94+ log .Debug ("management asset auto-updater skipped: control panel disabled" )
95+ return
96+ }
97+
98+ configPath , _ := schedulerConfigPath .Load ().(string )
99+ staticDir := StaticDir (configPath )
100+ EnsureLatestManagementHTML (ctx , staticDir , cfg .ProxyURL )
101+ }
102+
103+ runOnce ()
104+
105+ for {
106+ select {
107+ case <- ctx .Done ():
108+ return
109+ case <- ticker .C :
110+ runOnce ()
111+ }
112+ }
113+ }
114+
38115func newHTTPClient (proxyURL string ) * http.Client {
39116 client := & http.Client {Timeout : 15 * time .Second }
40117
@@ -109,6 +186,11 @@ func EnsureLatestManagementHTML(ctx context.Context, staticDir string, proxyURL
109186 ctx = context .Background ()
110187 }
111188
189+ if disableControlPanel .Load () {
190+ log .Debug ("management asset sync skipped: control panel disabled by configuration" )
191+ return
192+ }
193+
112194 staticDir = strings .TrimSpace (staticDir )
113195 if staticDir == "" {
114196 log .Debug ("management asset sync skipped: empty static directory" )
0 commit comments