v0.11.8 — Per-App Cross-Drive Backup (3-2-1 rule)
New feature: backup app data to a secondary storage drive to satisfy
the "different media" requirement of the 3-2-1 backup rule.
- settings.go: CrossDriveBackup struct, AppBackupPrefs.CrossDrive field,
getter/setter methods, GetOrCreateCrossDrivePassword, preserves
cross-drive config when toggling nightly backup
- crossdrive.go (new): CrossDriveRunner with rsync and restic backends.
Validates destination (mount point, writable), prevents source/dest
overlap, per-app concurrency lock, persists last_run/status/size.
- main.go: wire CrossDriveRunner, register cross-drive-daily (03:30)
and cross-drive-weekly (04:30 Sundays) scheduler jobs
- router.go: 4 new API endpoints — save config, trigger run, get status,
run-all. Router now accepts Settings and CrossDriveRunner.
- server.go: Server struct accepts CrossDriveRunner, new web route
POST /settings/cross-backup/{name}
- handlers.go: deployHandler populates CrossDriveConfig, BackupDestPaths,
BackupDestWarning, AppBackupEnabled. settingsCrossBackupHandler saves
config. backupsHandler builds CrossDriveSummary, UnconfiguredApps,
CrossDriveWarnings for backup page.
- deploy.html: "Biztonsági mentés" card with destination/method/schedule
dropdowns, last-run status, manual trigger button, flash messages.
- backups.html: "Másolatok másik meghajtóra" section with per-app
status rows, unconfigured app warnings, "Összes futtatása most" button.
- style.css: margin-bottom fix for .deploy-stale-data, new cross-drive
card and list styles.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,12 +116,13 @@ func main() {
|
||||
|
||||
// --- Initialize backup manager ---
|
||||
var backupMgr *backup.Manager
|
||||
stackProv := &stackAdapter{
|
||||
mgr: stackMgr,
|
||||
getStoragePaths: func() []settings.StoragePath { return sett.GetStoragePaths() },
|
||||
}
|
||||
if cfg.Backup.Enabled {
|
||||
backupMgr = backup.NewManager(cfg, pinger, sett, logger)
|
||||
backupMgr.SetStackProvider(&stackAdapter{
|
||||
mgr: stackMgr,
|
||||
getStoragePaths: func() []settings.StoragePath { return sett.GetStoragePaths() },
|
||||
})
|
||||
backupMgr.SetStackProvider(stackProv)
|
||||
backupMgr.AfterBackup = func() {
|
||||
nextDBDump := scheduler.NextDailyRun(cfg.Backup.DBDumpSchedule)
|
||||
nextBackup := scheduler.NextDailyRun(cfg.Backup.ResticSchedule)
|
||||
@@ -130,6 +131,9 @@ func main() {
|
||||
go backupMgr.LoadSnapshotHistory()
|
||||
}
|
||||
|
||||
// --- Initialize cross-drive backup runner ---
|
||||
crossDriveRunner := backup.NewCrossDriveRunner(sett, stackProv, logger)
|
||||
|
||||
// --- Initialize alert manager ---
|
||||
alertMgr := web.NewAlertManager(logger)
|
||||
|
||||
@@ -212,6 +216,18 @@ func main() {
|
||||
})
|
||||
}
|
||||
|
||||
// Cross-drive backup — daily at 03:30 (after main backup at 03:00)
|
||||
sched.Daily("cross-drive-daily", "03:30", func(ctx context.Context) error {
|
||||
return crossDriveRunner.RunAllScheduled(ctx, "daily")
|
||||
})
|
||||
// Cross-drive weekly — Sunday 04:30 (after integrity check at 04:00)
|
||||
sched.Daily("cross-drive-weekly", "04:30", func(ctx context.Context) error {
|
||||
if time.Now().Weekday() != time.Sunday {
|
||||
return nil
|
||||
}
|
||||
return crossDriveRunner.RunAllScheduled(ctx, "weekly")
|
||||
})
|
||||
|
||||
// Metrics prune — daily at 04:00
|
||||
if metricsStore != nil {
|
||||
sched.Daily("metrics-prune", "04:00", func(ctx context.Context) error {
|
||||
@@ -300,10 +316,10 @@ func main() {
|
||||
}()
|
||||
|
||||
// --- Initialize API router ---
|
||||
apiRouter := api.NewRouter(cfg, stackMgr, syncer, cpuCollector, backupMgr, metricsStore, logger)
|
||||
apiRouter := api.NewRouter(cfg, sett, stackMgr, syncer, cpuCollector, backupMgr, crossDriveRunner, metricsStore, logger)
|
||||
|
||||
// --- Initialize web server ---
|
||||
webServer := web.NewServer(cfg, stackMgr, cpuCollector, backupMgr, sched, sett, alertMgr, notifier, logger, Version)
|
||||
webServer := web.NewServer(cfg, stackMgr, cpuCollector, backupMgr, crossDriveRunner, sched, sett, alertMgr, notifier, logger, Version)
|
||||
|
||||
// --- Build HTTP mux ---
|
||||
mux := http.NewServeMux()
|
||||
|
||||
Reference in New Issue
Block a user