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:
@@ -2238,6 +2238,7 @@ a.stat-card:hover {
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.deploy-stale-data h4 {
|
||||
@@ -2265,3 +2266,67 @@ a.stat-card:hover {
|
||||
.btn-danger:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* Cross-drive backup card on deploy page */
|
||||
.deploy-cross-drive {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.deploy-cross-drive h4 {
|
||||
margin: 0 0 1rem 0;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.cross-drive-nightly {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
/* Cross-drive list on backup page */
|
||||
.cross-drive-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.cross-drive-item {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius);
|
||||
padding: .75rem 1rem;
|
||||
}
|
||||
|
||||
.cross-drive-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.cross-drive-name {
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cross-drive-name:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.cross-drive-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .4rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cross-drive-actions {
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user