// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base. Object.assign(BackupPage.prototype, { renderConfiguration() { const body = document.getElementById('backup-configuration-body'); if (!body) return; // Dismissed state is persisted server-side via Dismissible // (data/ui-state.json), so it follows the user across browsers/devices. // Banner + its divider are omitted entirely once dismissed. const warningDismissed = !!window.Dismissible?.isDismissed('backup-config-warning'); const warningHTML = warningDismissed ? '' : `
Keep your LibrePortal config backed up offline. Repository passwords live inside the config directory. Without that backup, the others cannot be decrypted by anyone — including you.
`; body.innerHTML = ` ${warningHTML}
`; this.invokeConfigManager(); }, async invokeConfigManager(attempt = 0) { if (window.configManager && typeof window.configManager.renderConfig === 'function') { try { await window.configManager.renderConfig('backup'); this.enhanceConfigurationWithPresets(); } catch (err) { console.error('Backup configuration render failed:', err); } return; } if (attempt >= 20) { const sec = document.getElementById('config-section'); if (sec) sec.innerHTML = `
Configuration system not loaded. Try refreshing the page.
`; return; } setTimeout(() => this.invokeConfigManager(attempt + 1), 150); }, async exportRepositoryPasswords(triggerBtn) { const restoreBtn = () => { if (triggerBtn) { triggerBtn.disabled = false; triggerBtn.dataset.busy = ''; } }; if (triggerBtn) { triggerBtn.disabled = true; triggerBtn.dataset.busy = '1'; } try { const task = await this.taskManager?.createTask( 'libreportal webui generate backup', 'webui', null ); if (task?.id) { await this.waitForTask(task.id, 20000); } const res = await fetch(`/data/backup/generated/passwords.txt?t=${Date.now()}`, { credentials: 'same-origin' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const text = await res.text(); if (!text || !text.includes('CFG_BACKUP_LOC_')) { throw new Error('Password file is empty — no locations configured?'); } const blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); const host = (window.systemConfigs?.CFG_INSTALL_NAME || 'libreportal').replace(/[^a-z0-9_-]/gi, '_'); const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-'); a.href = url; a.download = `libreportal-backup-passwords-${host}-${stamp}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); this.notify('Password export downloaded — store it offline.', 'success'); } catch (err) { this.notify(`Export failed: ${err.message || err}`, 'error'); } finally { restoreBtn(); } }, waitForTask(taskId, timeoutMs = 15000) { return new Promise((resolve) => { let done = false; const finish = () => { if (done) return; done = true; window.removeEventListener('taskCompleted', onComplete); clearTimeout(timer); resolve(); }; const onComplete = (e) => { if (e?.detail?.taskId === taskId) finish(); }; window.addEventListener('taskCompleted', onComplete); const timer = setTimeout(finish, timeoutMs); }); }, });