fix(webui): embedded backup-center review fixes

- BackupPage task-refresh run-guard is instance-relative now (window.backupPage
  OR window.overviewBackupPage), so the embedded center's own auto-refresh works
  instead of relying on OverviewManager's overlapping coverage.
- _ensureBackupAssets no longer memoizes a rejected promise — a transient
  script-load failure no longer bricks the backup center until reload; the next
  open retries.
- spaClean.loadScript removes the failed <script> element on error so the
  getElementById dedupe can't make a retry resolve without re-fetching.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-06-01 01:55:09 +01:00
parent 58eba94628
commit efdbed8e0c
3 changed files with 13 additions and 3 deletions

View File

@ -417,7 +417,11 @@ class OverviewManager {
'/core/backup-card/js/backup-app-card.js', '/core/backup-card/js/backup-app-card.js',
]; ];
const load = (url) => (window.spaClean && window.spaClean.loadScript) ? window.spaClean.loadScript(url) : Promise.resolve(); const load = (url) => (window.spaClean && window.spaClean.loadScript) ? window.spaClean.loadScript(url) : Promise.resolve();
this._backupAssets = scripts.reduce((p, url) => p.then(() => load(url)), Promise.resolve()); const chain = scripts.reduce((p, url) => p.then(() => load(url)), Promise.resolve());
// Don't memoize a rejection — a transient script-load failure would otherwise
// brick the backup center for the whole session. Clear the memo on failure so
// the next open retries.
this._backupAssets = chain.catch((e) => { this._backupAssets = null; throw e; });
return this._backupAssets; return this._backupAssets;
} }

View File

@ -79,7 +79,11 @@ class BackupPage {
match: (d) => ['backup', 'restore', 'delete', 'delete_all'].includes(d.action) match: (d) => ['backup', 'restore', 'delete', 'delete_all'].includes(d.action)
|| /^libreportal\s+(backup|restore)\b/.test((d.task && d.task.command) || ''), || /^libreportal\s+(backup|restore)\b/.test((d.task && d.task.command) || ''),
run: () => { run: () => {
if (window.backupPage === this && document.getElementById('backup-page')) { // Works whether this is the standalone page (window.backupPage) or
// the instance embedded in the Overview Backups tab
// (window.overviewBackupPage) — the hard-coded global alone would
// make the embedded center's auto-refresh dead.
if ((window.backupPage === this || window.overviewBackupPage === this) && document.getElementById('backup-page')) {
return this.refreshAll().then(() => this.render()); return this.refreshAll().then(() => this.render());
} }
}, },

View File

@ -544,7 +544,9 @@ class LibrePortalSPAClean {
script.src = src; script.src = src;
script.id = scriptId; script.id = scriptId;
script.onload = resolve; script.onload = resolve;
script.onerror = reject; // Remove the element on failure so the getElementById() dedupe above can't
// make a later retry resolve instantly without re-fetching.
script.onerror = (e) => { try { script.remove(); } catch (_) {} reject(e); };
document.head.appendChild(script); document.head.appendChild(script);
}); });
} }