diff --git a/containers/libreportal/frontend/html/backup-content.html b/containers/libreportal/frontend/html/backup-content.html index c7ea0c0..6e974e4 100644 --- a/containers/libreportal/frontend/html/backup-content.html +++ b/containers/libreportal/frontend/html/backup-content.html @@ -83,9 +83,15 @@
-

Per-app status

- Latest backup per app on this host +

Backup status

+ System config and every installed app's latest backup. System config always first — a bare-metal restore needs it.
+
@@ -96,31 +102,6 @@
-
-
-

- - - - - - System config -

- Global settings, WebUI login & backup-location credentials -
-
- No backup yet -
-

- Snapshot the LibrePortal system config to every enabled location so a bare-metal - restore is self-sufficient — without it, the credentials needed to reach your own - backups live only on this box. Runs automatically with “Backup all apps” too. -

-
- - -
-
diff --git a/containers/libreportal/frontend/js/components/backup/backup-page.js b/containers/libreportal/frontend/js/components/backup/backup-page.js index 9f5d63b..707f9c5 100644 --- a/containers/libreportal/frontend/js/components/backup/backup-page.js +++ b/containers/libreportal/frontend/js/components/backup/backup-page.js @@ -456,7 +456,7 @@ class BackupPage { subtitleFor(tab) { return { - dashboard: 'Per-app status and storage at a glance.', + dashboard: 'Backup status — system config + every app — at a glance.', backups: 'Every snapshot across every enabled location.', locations: 'Where backups are stored. Add, edit, or remove destinations.', migrate: 'Restore an app from another LibrePortal that shares one of your backup locations.', @@ -586,20 +586,14 @@ class BackupPage { ${this.tile('Total stored', this.formatBytes(totalSize), 'deduplicated, encrypted')} `; + // System config tile is rendered FIRST so the bare-metal-restore + // prerequisite is always at eye-level — without it, the user's + // backups exist but the credentials needed to reach them don't. + const systemTileHtml = this.renderSystemTile(d.system || {}); if (!apps.length) { - appGrid.innerHTML = `
No apps installed yet.
`; + appGrid.innerHTML = systemTileHtml + `
No apps installed yet.
`; } else { - appGrid.innerHTML = apps.map(app => this.renderAppTile(app)).join(''); - } - - const sysStatus = document.getElementById('backup-system-status'); - if (sysStatus) { - const sys = d.system || {}; - const hasSys = !!sys.latest_snapshot; - sysStatus.innerHTML = ` - - ${hasSys ? 'Last backed up ' + this.formatRelative(sys.latest_time) : 'No backup yet'} - `; + appGrid.innerHTML = systemTileHtml + apps.map(app => this.renderAppTile(app)).join(''); } if (!locs.length) { @@ -620,6 +614,50 @@ class BackupPage { } } + // System config tile — same shape as an app tile but with a server icon + // and two inline action buttons (the standalone System config card used + // to host these). Rendered first in the Backup status grid so it always + // sits at eye-level. + renderSystemTile(sys) { + const has = !!sys.latest_snapshot; + const dot = has ? 'ok' : 'none'; + const when = has ? 'Last backed up ' + this.formatRelative(sys.latest_time) : 'No backup yet'; + return ` +
+
+ + + + + + +
+
+
System config
+
+ + ${this.escape(when)} +
+
+
+ + +
+
+ `; + } + tile(label, value, detail) { return `