controller v0.46.0: fix /backups 500 — strip dead disk-tier UI from backups.html

backups.html still referenced .Backup.{RepoStats,LastBackup,ResticSchedule,
NextBackup,PruneSchedule,Retention,SnapshotHistory,LastCheckTime,LastCheckOK} —
fields removed from FullBackupStatus in the 8C de-privileging (disk-tier backup
moved to the agent). Field access on the slimmed struct 500s. Removed the dead
restic/snapshot/repo-stat sections; kept the app-data (DB dumps + per-app) view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 10:21:56 +02:00
parent f72f2c7ccb
commit 64dceea449
2 changed files with 36 additions and 135 deletions
+26
View File
@@ -1,5 +1,31 @@
## Changelog
### v0.46.0 — fix: /backups 500 (template referenced disk-tier fields stripped in 8C) (2026-06-12)
`GET /backups` returned **HTTP 500**. Root cause (from the live log, not guessed):
`backups.html:64: executing "backups" at <.Backup.RepoStats>: can't evaluate field RepoStats in type
interface {}`. The 8C de-privileging slimmed `FullBackupStatus` to **app-data only** (DB dumps +
Docker-volume tars; the disk-tier restic/cross-drive backup moved to the host agent), but
`backups.html` still carried the full pre-8C restic UI. It referenced `.Backup.X` struct fields that no
longer exist: `RepoStats, LastBackup, ResticSchedule, NextBackup, PruneSchedule, Retention,
SnapshotHistory, LastCheckTime, LastCheckOK`. While those fields existed-but-nil, `{{if .Backup.X}}`
short-circuited safely; once the fields were *removed from the struct*, the field access itself errors →
500. (Not a panic, not a funcmap/nil-subfield issue; root-level map keys like `.PerDriveRepoStats` are
map lookups → nil on miss → safe, and the `Tier1*/Tier2*` fields are on `AppBackupRows`, still supplied.)
Fix — removed the dead disk-tier UI from `backups.html`, keeping the app-data backup view:
- Section 0 storage-stats: dropped "Mentési tároló" + "Pillanatképek" (RepoStats); kept "DB mentések".
- Section 1 cards: the status card now keys on `.Backup.LastDBDump` (was `.Backup.LastBackup`); removed
the "Tároló méret" card.
- Section 2 schedule: removed the "Restic pillanatkép" + "Karbantartás" rows and the
restic-last-backup/retention summary; kept the DB-dump schedule + a DB-dump last-run summary.
- Section 5 "Pillanatképek" (restic snapshot history): removed entirely.
- Section 6 "1. szint" tier: removed the per-drive/repo-stats + integrity rows (relabeled "(restic)" →
"(adatbázis + konfiguráció)"); kept the DB-dump-count row.
No Go change (`FullBackupStatus` was already correct); template-only. `settings.html`'s `.ResticSchedule`/
`.LastCheckTime` are unaffected — they're root-map lookups (nil-safe), not struct-field access.
### v0.45.0 — storage UX polish: deterministic order, init filter, register shortcut, system-storage clarity (2026-06-12)
Builds on v0.44.0's role-aware drive management. Pairs with felhom-agent v0.24.0 (the eject role-gate
+10 -135
View File
@@ -61,42 +61,32 @@
{{end}}
</div>
<div class="storage-stats">
{{if .Backup.RepoStats}}
<div class="storage-stat-row">
<span class="storage-stat-label">Mentési tároló</span>
<span class="storage-stat-value mono">{{.Backup.RepoStats.TotalSize}}</span>
</div>
{{end}}
<div class="storage-stat-row">
<span class="storage-stat-label">DB mentések</span>
<span class="storage-stat-value mono">{{if .Backup.DumpFiles}}{{len .Backup.DumpFiles}} fájl{{else}}{{end}}</span>
</div>
<div class="storage-stat-row">
<span class="storage-stat-label">Pillanatképek</span>
<span class="storage-stat-value mono">{{if .Backup.RepoStats}}{{.Backup.RepoStats.SnapshotCount}}{{else}}0{{end}}</span>
</div>
</div>
</div>
</div>
<!-- Section 1: Status overview cards -->
<div class="stats-grid backup-page-cards">
{{if .Backup.LastBackup}}
{{if .Backup.LastBackup.Success}}
{{if .Backup.LastDBDump}}
{{if .Backup.LastDBDump.Success}}
<div class="stat-card stat-running">
<div class="stat-value">&#10003;</div>
<div class="stat-label">Helyi mentés aktív</div>
<div class="stat-label">Adatmentés aktív</div>
</div>
{{else}}
<div class="stat-card stat-stopped">
<div class="stat-value">&#10007;</div>
<div class="stat-label">Helyi mentés sikertelen</div>
<div class="stat-label">Adatmentés sikertelen</div>
</div>
{{end}}
{{else}}
<div class="stat-card">
<div class="stat-value"></div>
<div class="stat-label">Helyi mentés</div>
<div class="stat-label">Adatmentés</div>
</div>
{{end}}
@@ -111,15 +101,6 @@
</div>
<div class="stat-label">Adatbázis mentve</div>
</div>
<div class="stat-card stat-total">
<div class="stat-value">
{{if .Backup.RepoStats}}{{.Backup.RepoStats.TotalSize}}{{else}}{{end}}
</div>
<div class="stat-label">Tároló méret
{{if .Backup.RepoStats}}<br><span class="relative-time">{{.Backup.RepoStats.SnapshotCount}} pillanatkép</span>{{end}}
</div>
</div>
</div>
<!-- Section 2: Schedule -->
@@ -131,37 +112,19 @@
<span class="schedule-time">{{.Backup.DBDumpSchedule}}</span>
<span class="schedule-next">Következő: {{nextRunLabel .Backup.NextDBDump}}</span>
</div>
<div class="schedule-row">
<span class="schedule-task">Restic pillanatkép</span>
<span class="schedule-time">{{.Backup.ResticSchedule}}</span>
<span class="schedule-next">Következő: {{nextRunLabel .Backup.NextBackup}}</span>
</div>
<div class="schedule-row">
<span class="schedule-task">Karbantartás</span>
<span class="schedule-time">{{pruneLabel .Backup.PruneSchedule}}</span>
<span class="schedule-next">Következő: {{nextPruneLabel .Backup.PruneSchedule}}</span>
</div>
</div>
<div class="schedule-summary">
{{if .Backup.LastBackup}}
{{if .Backup.LastDBDump}}
<div class="schedule-summary-row">
<span>Utolsó sikeres mentés:</span>
<span class="schedule-summary-value">{{fmtTime .Backup.LastBackup.LastRun}} ({{timeAgo .Backup.LastBackup.LastRun}})</span>
</div>
<div class="schedule-summary-row">
<span>Mentés időtartam:</span>
<span class="schedule-summary-value">{{fmtDuration .Backup.LastBackup.Duration}}</span>
<span>Utolsó adatbázis mentés:</span>
<span class="schedule-summary-value">{{fmtTime .Backup.LastDBDump.LastRun}} ({{timeAgo .Backup.LastDBDump.LastRun}})</span>
</div>
{{else}}
<div class="schedule-summary-row">
<span>Utolsó sikeres mentés:</span>
<span>Utolsó adatbázis mentés:</span>
<span class="schedule-summary-value relative-time">Még nem futott</span>
</div>
{{end}}
<div class="schedule-summary-row">
<span>Megőrzés:</span>
<span class="schedule-summary-value">{{.Backup.Retention.KeepDaily}} napi · {{.Backup.Retention.KeepWeekly}} heti · {{.Backup.Retention.KeepMonthly}} havi</span>
</div>
</div>
<div class="schedule-actions">
<button class="btn btn-sm btn-primary" onclick="triggerBackupFromPage()" id="backup-page-btn"
@@ -364,43 +327,6 @@
</div>
{{end}}
<!-- Section 5: Snapshots -->
<div class="backup-section-card">
<h3>Pillanatképek</h3>
{{if .Backup.SnapshotHistory}}
<div class="backup-table-wrap">
<table class="snapshot-table">
<thead>
<tr>
<th>Azonosító</th>
<th>Időpont</th>
<th>Hozzáadott <span class="col-subtitle">(új adat)</span></th>
<th>Új fájl</th>
<th>Változott</th>
</tr>
</thead>
<tbody>
{{range .Backup.SnapshotHistory}}
<tr>
<td class="mono">{{shortID .SnapshotID}}</td>
<td class="mono">{{fmtTime .Time}}</td>
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{else}}0{{end}}</td>
<td class="mono">{{if .HasStats}}{{.FilesNew}}{{else}}0{{end}}</td>
<td class="mono">{{if .HasStats}}{{.FilesChanged}}{{else}}0{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="snapshot-footer">
Összesen: {{len .Backup.SnapshotHistory}} pillanatkép
{{if .Backup.RepoStats}} · {{.Backup.RepoStats.TotalSize}}{{end}}
</div>
{{else}}
<div class="backup-table-empty">Még nincs pillanatkép.</div>
{{end}}
</div>
<!-- Section 6: Részletek (Details) -->
<div class="repo-card">
<h3>Részletek</h3>
@@ -409,65 +335,14 @@
<div class="details-tier">
<div class="details-tier-header" onclick="toggleTier(this)">
<span class="expand-icon"></span>
<h4 class="repo-tier-title">1. szint — Helyi mentés (restic)</h4>
<h4 class="repo-tier-title">1. szint — Helyi mentés (adatbázis + konfiguráció)</h4>
</div>
<div class="details-tier-body">
{{if .PerDriveRepoStats}}
{{range .PerDriveRepoStats}}
<div class="drive-detail-card">
<div class="drive-detail-header">{{.DriveLabel}}</div>
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Méret:</span>
<span class="repo-value">{{if .TotalSize}}{{.TotalSize}}{{else}}—{{end}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Pillanatképek:</span>
<span class="repo-value">{{.SnapshotCount}}</span>
</div>
</div>
</div>
{{end}}
{{if gt (len .PerDriveRepoStats) 1}}
<div class="repo-info-rows" style="margin-top:0.5rem;padding-top:0.5rem;border-top:1px solid var(--border-color)">
<div class="repo-info-row">
<span class="repo-label">Összesen:</span>
<span class="repo-value">{{if .Backup.RepoStats}}{{.Backup.RepoStats.TotalSize}} · {{.Backup.RepoStats.SnapshotCount}} pillanatkép{{end}}</span>
</div>
</div>
{{end}}
{{else}}
{{if .Backup.RepoStats}}
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Méret:</span>
<span class="repo-value">{{.Backup.RepoStats.TotalSize}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Pillanatképek:</span>
<span class="repo-value">{{.Backup.RepoStats.SnapshotCount}}</span>
</div>
</div>
{{end}}
{{end}}
<div class="repo-info-rows" style="margin-top:0.5rem">
<div class="repo-info-row">
<span class="repo-label">Adatbázis mentések:</span>
<span class="repo-value">{{if .Backup.DumpFiles}}{{len .Backup.DumpFiles}} dump fájl{{if gt .DBDumpTotalBytes 0}} — {{fmtBytes .DBDumpTotalBytes}}{{end}}{{else}}Nincs dump fájl{{end}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Integritás:</span>
<span class="repo-value">
{{if .Backup.LastCheckTime.IsZero}}
<span class="relative-time">Még nem ellenőrzött</span>
{{else if .Backup.LastCheckOK}}
<span class="backup-status-ok">Rendben</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
{{else}}
<span class="backup-status-fail">Hiba</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
{{end}}
</span>
</div>
</div>
<!-- Encryption key -->