// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base. Object.assign(BackupPage.prototype, { openRestoreModal(app, locIdx, snapshot) { const locName = this.locName(locIdx); const modal = document.getElementById('backup-restore-modal'); const body = document.getElementById('backup-restore-modal-body'); if (!modal || !body) return; body.innerHTML = `
Restore ${this.escape(app)} from backup ${this.escape(snapshot)} at ${this.escape(locName)}?
The app will be stopped, its folder wiped, the backup restored in place, then the app started again. App-specific pre/post-restore hooks run if present.
`; modal.dataset.app = app; modal.dataset.locIdx = locIdx; modal.dataset.snapshot = snapshot; modal.classList.add('open'); }, openDeleteModal(app, locIdx, snapshot) { const locName = this.locName(locIdx); const modal = document.getElementById('backup-delete-modal'); const body = document.getElementById('backup-delete-modal-body'); if (!modal || !body) return; body.innerHTML = `Delete backup ${this.escape(snapshot)} for ${this.escape(app)} from ${this.escape(locName)}?
This cannot be undone. Append-only locations will reject the operation.
`; modal.dataset.app = app; modal.dataset.locIdx = locIdx; modal.dataset.snapshot = snapshot; modal.classList.add('open'); }, async confirmRestore() { const modal = document.getElementById('backup-restore-modal'); const { app, locIdx, snapshot } = modal.dataset; this.closeAllModals(); await this.runTask(`libreportal restore app start ${app} ${snapshot} ${locIdx}`, 'restore', app); }, async confirmDelete() { const modal = document.getElementById('backup-delete-modal'); const { app, locIdx, snapshot } = modal.dataset; this.closeAllModals(); await this.runTask(`libreportal backup app delete ${app} ${locIdx}:${snapshot}`, 'backup', app); }, async runBackupAllApps() { 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); }, openBackupPickModal(opts = {}) { const modal = document.getElementById('backup-pick-modal'); const body = document.getElementById('backup-pick-modal-body'); if (!modal || !body) return; const apps = this.dashboard?.apps || []; const sys = this.dashboard?.system || {}; const preTickSystem = !!opts.preTickSystem; const preTickApps = new Set(opts.preTickApps || []); // System row at the top — uses the LibrePortal icon to match the // dashboard tile. Then every installed app, alphabetical. const sortedApps = apps.slice().sort((a, b) => a.app.localeCompare(b.app)); const row = (key, iconSrc, label, sub, checked) => ` `; const sysSub = sys.latest_snapshot ? 'Last backed up ' + this.formatRelative(sys.latest_time) : 'No backup yet'; const rows = [ row('__system__', '/core/icons/apps/libreportal.svg', 'Configs', sysSub, preTickSystem), ...sortedApps.map(app => { const meta = this.appMeta(app.app); const sub = app.latest_snapshot ? 'Last backed up ' + this.formatRelative(app.latest_time) : 'No backup yet'; return row(app.app, meta.icon, meta.displayName, sub, preTickApps.has(app.app)); }) ].join(''); body.innerHTML = `Pick what to snapshot. Each selection runs in the background and shows up on the Tasks page.