v0.12.4 — 15 bug fixes (CRITICAL/HIGH/MEDIUM)
CRITICAL: - C1: SetAppBackupBulk data loss + nil map panic (settings.go) - C2: UpdateStackConfig nil Env map panic (deploy.go) - C3: ValidateDump missing scanner.Err() check (dbdump.go) HIGH: - H1: nextDailyRun DST bug — use time.Date(day+1) not Add(24h) - H2: Cache Europe/Budapest timezone with sync.Once in scheduler - H3: settings.save() leaks .tmp file on WriteFile failure - H4: SetNotificationPrefs nil pointer panic - H5: appDirSize + getDirSizeBytes ignore Sscanf return value - H6: getDirSizeBytes has no timeout — add 30s context - H7: dbdump.go tmpFile not using defer Close - H8: UpdateCrossDriveStatus misleading comment MEDIUM: - M1: Replace custom containsBytes with strings.Contains - M2: scheduler.Every() validates interval > 0 - M3: executeJob panic recovery now sets LastRun - M4: logPostStartStatus copies env slice before goroutine - M5: Cache timezone in web package via getTimezone() sync.Once Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,7 @@ func (s *Settings) save() error {
|
||||
}
|
||||
|
||||
if err := os.WriteFile(tmpPath, data, 0644); err != nil {
|
||||
os.Remove(tmpPath) // clean up partial file
|
||||
return fmt.Errorf("writing tmp settings: %w", err)
|
||||
}
|
||||
|
||||
@@ -215,6 +216,9 @@ func (s *Settings) GetNotificationPrefs() *NotificationPrefs {
|
||||
// SetNotificationPrefs updates notification preferences and saves to disk.
|
||||
// H17: Deep-copies prefs so caller mutations after the call don't affect stored state.
|
||||
func (s *Settings) SetNotificationPrefs(prefs *NotificationPrefs) error {
|
||||
if prefs == nil {
|
||||
return fmt.Errorf("notification preferences cannot be nil")
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
copy := *prefs
|
||||
@@ -267,17 +271,18 @@ func (s *Settings) GetAppBackupMap() map[string]bool {
|
||||
}
|
||||
|
||||
// SetAppBackupBulk updates backup prefs for all stacks at once and saves to disk.
|
||||
// Preserves existing CrossDrive configs.
|
||||
// Preserves existing CrossDrive configs and stacks not present in the input.
|
||||
func (s *Settings) SetAppBackupBulk(prefs map[string]bool) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
newMap := make(map[string]AppBackupPrefs, len(prefs))
|
||||
if s.AppBackup == nil {
|
||||
s.AppBackup = make(map[string]AppBackupPrefs)
|
||||
}
|
||||
for name, enabled := range prefs {
|
||||
existing := s.AppBackup[name] // preserves CrossDrive
|
||||
existing.Enabled = enabled
|
||||
newMap[name] = existing
|
||||
s.AppBackup[name] = existing
|
||||
}
|
||||
s.AppBackup = newMap
|
||||
return s.save()
|
||||
}
|
||||
|
||||
@@ -321,7 +326,8 @@ func (s *Settings) SetCrossDriveConfig(stackName string, cfg *CrossDriveBackup)
|
||||
}
|
||||
|
||||
// UpdateCrossDriveStatus updates runtime status fields for a cross-drive backup in-place.
|
||||
// fn receives a pointer to the CrossDriveBackup (creates one if nil) and may mutate it.
|
||||
// fn receives a pointer to the CrossDriveBackup and may mutate it.
|
||||
// If no cross-drive config exists for the stack, does nothing and returns nil.
|
||||
func (s *Settings) UpdateCrossDriveStatus(stackName string, fn func(*CrossDriveBackup)) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user