EnsureLatestManagementHTML checks the latest management.html asset and updates the local copy when needed. It coalesces concurrent sync attempts and returns whether the asset exists after the sync attempt.
(ctx context.Context, staticDir string, proxyURL string, panelRepository string)
| 195 | // EnsureLatestManagementHTML checks the latest management.html asset and updates the local copy when needed. |
| 196 | // It coalesces concurrent sync attempts and returns whether the asset exists after the sync attempt. |
| 197 | func EnsureLatestManagementHTML(ctx context.Context, staticDir string, proxyURL string, panelRepository string) bool { |
| 198 | if ctx == nil { |
| 199 | ctx = context.Background() |
| 200 | } |
| 201 | |
| 202 | staticDir = strings.TrimSpace(staticDir) |
| 203 | if staticDir == "" { |
| 204 | log.Debug("management asset sync skipped: empty static directory") |
| 205 | return false |
| 206 | } |
| 207 | localPath := filepath.Join(staticDir, managementAssetName) |
| 208 | |
| 209 | _, _, _ = sfGroup.Do(localPath, func() (interface{}, error) { |
| 210 | lastUpdateCheckMu.Lock() |
| 211 | now := time.Now() |
| 212 | timeSinceLastAttempt := now.Sub(lastUpdateCheckTime) |
| 213 | if !lastUpdateCheckTime.IsZero() && timeSinceLastAttempt < managementSyncMinInterval { |
| 214 | lastUpdateCheckMu.Unlock() |
| 215 | log.Debugf( |
| 216 | "management asset sync skipped by throttle: last attempt %v ago (interval %v)", |
| 217 | timeSinceLastAttempt.Round(time.Second), |
| 218 | managementSyncMinInterval, |
| 219 | ) |
| 220 | return nil, nil |
| 221 | } |
| 222 | lastUpdateCheckTime = now |
| 223 | lastUpdateCheckMu.Unlock() |
| 224 | |
| 225 | localFileMissing := false |
| 226 | if _, errStat := os.Stat(localPath); errStat != nil { |
| 227 | if errors.Is(errStat, os.ErrNotExist) { |
| 228 | localFileMissing = true |
| 229 | } else { |
| 230 | log.WithError(errStat).Debug("failed to stat local management asset") |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | if errMkdirAll := os.MkdirAll(staticDir, 0o755); errMkdirAll != nil { |
| 235 | log.WithError(errMkdirAll).Warn("failed to prepare static directory for management asset") |
| 236 | return nil, nil |
| 237 | } |
| 238 | |
| 239 | releaseURL := resolveReleaseURL(panelRepository) |
| 240 | client := newHTTPClient(proxyURL) |
| 241 | |
| 242 | localHash, err := fileSHA256(localPath) |
| 243 | if err != nil { |
| 244 | if !errors.Is(err, os.ErrNotExist) { |
| 245 | log.WithError(err).Debug("failed to read local management asset hash") |
| 246 | } |
| 247 | localHash = "" |
| 248 | } |
| 249 | |
| 250 | asset, remoteHash, err := fetchLatestAsset(ctx, client, releaseURL) |
| 251 | if err != nil { |
| 252 | if localFileMissing { |
| 253 | log.WithError(err).Warn("failed to fetch latest management release information, trying fallback page") |
| 254 | if ensureFallbackManagementHTML(ctx, client, localPath) { |
no test coverage detected