// 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);
});
},
});