feat(webui): surface system-config backup/restore on the backup dashboard

Add a "System config" card to the backup dashboard with two actions wired through
the task processor (same path as "Backup all apps"):

- "Back up now"  -> libreportal backup system
- "Restore…"     -> libreportal restore system  (confirm dialog explains it lands
  in a staging folder and never overwrites live config)

Card copy explains why it matters (the backup-location creds otherwise live only on
the box). Click handlers + runBackupSystem/confirmRestoreSystem added; JS parses,
data-actions match handlers, commands match the CLI subcommands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-26 00:31:23 +01:00
parent 88f6ce7820
commit c2c10103b8
2 changed files with 34 additions and 0 deletions

View File

@ -88,6 +88,21 @@
<div class="backup-repo-list" id="backup-repo-list-summary"></div>
</div>
</div>
<div class="backup-card">
<div class="backup-card-header">
<h2>System config</h2>
<span class="backup-card-hint">Global settings, WebUI login &amp; backup-location credentials</span>
</div>
<p class="backup-card-hint" style="margin:0 0 12px">
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.
</p>
<div class="backup-system-actions" style="display:flex;gap:8px;flex-wrap:wrap">
<button type="button" class="backup-primary-btn" data-action="backup-system">Back up now</button>
<button type="button" class="backup-secondary-btn" data-action="restore-system">Restore…</button>
</div>
</div>
</section>
<section class="backup-tabpanel" id="backup-panel-backups">

View File

@ -176,6 +176,16 @@ class BackupPage {
return;
}
if (e.target.closest('[data-action="backup-system"]')) {
this.runBackupSystem();
return;
}
if (e.target.closest('[data-action="restore-system"]')) {
this.confirmRestoreSystem();
return;
}
const restoreBtn = e.target.closest('[data-action="restore-snapshot"]');
if (restoreBtn) {
this.openRestoreModal(restoreBtn.dataset.app, restoreBtn.dataset.loc, restoreBtn.dataset.snapshot);
@ -1724,6 +1734,15 @@ class BackupPage {
await this.runTask(`libreportal backup all`, 'backup', null);
}
async runBackupSystem() {
await this.runTask(`libreportal backup system`, 'backup', null);
}
async confirmRestoreSystem() {
if (!confirm('Restore the latest system-config snapshot?\n\nIt is restored into a staging folder (not applied) — review it, then copy what you need (e.g. backup-location credentials, login, settings) into the live config. Your running config is NOT overwritten.')) return;
await this.runTask(`libreportal restore system`, 'restore', null);
}
async runTask(command, type, app) {
if (!this.taskManager) {
this.notify('Task system unavailable', 'error');