fix: deep bug hunt II — concurrency, security & optimization (25 files)
Critical: watchdog mutex panic safety, SetGeoAppOverride nil guard, SSD-only app DB restore fallback. High: double deploy race (atomic Deploying flag), delete/remove during deploy guard, ScanStacks overwrite protection, FileBrowser mount mutex, PushEvent history, PushOnce error handling, DB dump sync+close before rename, restic retry fresh context, encrypt failure logging, cross-backup path traversal validation, deepCopyStack completeness. Security: constant-time API key comparison, login rate limiting (5/min), git credential masking in logs, storage path prefix traversal fix. Concurrency: MigrateEncryption lock ordering, SubdomainInUse I/O outside lock, scheduler late-registered jobs, SQLite WAL verification, metrics shutdown context, telemetry scan error logging, asset sync lock scope. Optimization: streaming file copy for DB dumps, restic stats dedup, atomic infra config copy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ type Syncer struct {
|
||||
logger *log.Logger
|
||||
debug bool
|
||||
mu sync.Mutex
|
||||
running bool
|
||||
status SyncStatus
|
||||
}
|
||||
|
||||
@@ -70,7 +71,18 @@ func New(hubURL, apiKey, assetsDir, fallbackDir string, logger *log.Logger, debu
|
||||
// changed/new files. It also removes local files not in the Hub manifest.
|
||||
func (s *Syncer) Sync(ctx context.Context) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.running {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("asset sync already in progress")
|
||||
}
|
||||
s.running = true
|
||||
s.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
s.mu.Lock()
|
||||
s.running = false
|
||||
s.mu.Unlock()
|
||||
}()
|
||||
|
||||
s.logger.Println("[INFO] Asset sync starting...")
|
||||
|
||||
@@ -145,13 +157,15 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
// 5. Save local manifest copy
|
||||
s.saveLocalManifest(manifest)
|
||||
|
||||
// 6. Update status
|
||||
// 6. Update status (under lock)
|
||||
s.mu.Lock()
|
||||
s.status = SyncStatus{
|
||||
LastSync: time.Now().UTC().Format(time.RFC3339),
|
||||
LastStatus: "ok",
|
||||
FileCount: len(manifest.Files),
|
||||
TotalBytes: totalBytes,
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
s.logger.Printf("[INFO] Asset sync complete: %d downloaded, %d unchanged, %d removed (%d total files)",
|
||||
downloaded, skipped, removed, len(manifest.Files))
|
||||
@@ -187,6 +201,7 @@ func (s *Syncer) Status() SyncStatus {
|
||||
}
|
||||
|
||||
func (s *Syncer) setError(err error) {
|
||||
s.mu.Lock()
|
||||
s.status = SyncStatus{
|
||||
LastSync: time.Now().UTC().Format(time.RFC3339),
|
||||
LastStatus: "error",
|
||||
@@ -194,6 +209,7 @@ func (s *Syncer) setError(err error) {
|
||||
FileCount: s.status.FileCount,
|
||||
TotalBytes: s.status.TotalBytes,
|
||||
}
|
||||
s.mu.Unlock()
|
||||
s.logger.Printf("[WARN] Asset sync failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user