Phase 3 complete: per-app backup toggles, restore, storage overview

- Storage overview on backup page (SSD/HDD bars, repo stats)
- Restic password visibility + hub sync for disaster recovery
- App data discovery (HDD bind mounts, Docker volumes)
- Per-app backup toggle checkboxes with settings persistence
- Dynamic backup paths: enabled app HDD data included in restic snapshots
- Limited app restore from snapshots (self-service recovery)
- Snapshots API endpoint for restore dropdown
- Version bump to 0.8.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 21:29:56 +01:00
parent a3af7c6a2d
commit 7d801d1094
15 changed files with 1088 additions and 29 deletions
+54
View File
@@ -24,6 +24,14 @@ type Settings struct {
// Cached state
DBValidations map[string]DBValidationCache `json:"db_validations,omitempty"`
// Per-app backup preferences
AppBackup map[string]AppBackupPrefs `json:"app_backup,omitempty"`
}
// AppBackupPrefs holds per-app backup toggle state.
type AppBackupPrefs struct {
Enabled bool `json:"enabled"`
}
// NotificationPrefs holds customer notification preferences.
@@ -170,3 +178,49 @@ func (s *Settings) SetNotificationPrefs(prefs *NotificationPrefs) error {
s.Notifications = prefs
return s.save()
}
// IsAppBackupEnabled returns whether backup is enabled for the given stack.
func (s *Settings) IsAppBackupEnabled(stackName string) bool {
s.mu.RLock()
defer s.mu.RUnlock()
if s.AppBackup == nil {
return false
}
return s.AppBackup[stackName].Enabled
}
// SetAppBackup enables or disables backup for a stack and saves to disk.
func (s *Settings) SetAppBackup(stackName string, enabled bool) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.AppBackup == nil {
s.AppBackup = make(map[string]AppBackupPrefs)
}
s.AppBackup[stackName] = AppBackupPrefs{Enabled: enabled}
return s.save()
}
// GetAppBackupMap returns a map of stack_name -> enabled for all app backup prefs.
func (s *Settings) GetAppBackupMap() map[string]bool {
s.mu.RLock()
defer s.mu.RUnlock()
if s.AppBackup == nil {
return nil
}
result := make(map[string]bool, len(s.AppBackup))
for k, v := range s.AppBackup {
result[k] = v.Enabled
}
return result
}
// SetAppBackupBulk updates backup prefs for all stacks at once and saves to disk.
func (s *Settings) SetAppBackupBulk(prefs map[string]bool) error {
s.mu.Lock()
defer s.mu.Unlock()
s.AppBackup = make(map[string]AppBackupPrefs, len(prefs))
for name, enabled := range prefs {
s.AppBackup[name] = AppBackupPrefs{Enabled: enabled}
}
return s.save()
}