librelad 82989069e2 refactor(backup): decompose backup-page god-file into 13 responsibility files
Faithful prototype-augment split of backup-page.js (2353->753 line base) into
fetch-client, dashboard, snapshots, locations, location-fields, ssh-key,
retention-presets, configuration, engine-details, location-modal,
snapshot-actions, migrate (+ the earlier cron-schedule). Methods relocated
verbatim (mechanical sed/awk extraction, no logic change); all augment
BackupPage.prototype and load after the base via the ordered kernel loader.
Verified: all 99 original methods present exactly once across base+clusters,
no duplicates, all 14 files node --check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 14:02:45 +01:00

126 lines
5.9 KiB
JavaScript

// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base.
Object.assign(BackupPage.prototype, {
openLocationModal_unused(idx) {
const loc = (this.locations?.locations || []).find(l => l.idx === idx);
if (!loc) return;
const modal = document.getElementById('backup-location-modal');
const body = document.getElementById('backup-location-modal-body');
const title = document.getElementById('backup-location-modal-title');
if (!modal || !body) return;
modal.dataset.locIdx = idx;
title.textContent = `Edit location: ${loc.name}`;
body.innerHTML = `
<div class="config-category backup-location-config" data-section="location-${idx}">
<div id="backup-location-connection"></div>
</div>
<div class="config-category backup-location-config">
<h3>Retention</h3>
<p class="category-description">When to delete old backups from this location.</p>
<div id="backup-location-retention"></div>
</div>
`;
this.refreshLocationModalTypeFields(loc.type, loc);
this.refreshLocationModalRetention(loc.custom_retention);
modal.classList.add('open');
},
refreshLocationModalTypeFields(type, locOverride) {
const container = document.getElementById('backup-location-connection');
const modal = document.getElementById('backup-location-modal');
if (!container || !modal) return;
const idx = parseInt(modal.dataset.locIdx, 10);
const loc = locOverride || (this.locations?.locations || []).find(l => l.idx === idx) || {};
const suffixes = this.locFieldsForType(type);
container.innerHTML = this.renderLocFields(idx, suffixes, loc);
this.tagFieldsForSave(container);
},
refreshLocationModalRetention(enabled) {
const container = document.getElementById('backup-location-retention');
const modal = document.getElementById('backup-location-modal');
if (!container || !modal) return;
const idx = parseInt(modal.dataset.locIdx, 10);
const loc = (this.locations?.locations || []).find(l => l.idx === idx) || {};
// The "Use custom retention" toggle itself stays at the top regardless.
const toggleField = this.renderLocFields(idx, ['CUSTOM_RETENTION'], loc);
if (!enabled) {
container.innerHTML = `
${toggleField}
<div class="backup-card-hint" style="margin-top:8px">Inherits the <strong>global retention policy</strong> from the Configuration tab.</div>
`;
this.tagFieldsForSave(container);
return;
}
const values = {
last: loc.keep_last || '',
daily: loc.keep_daily || '',
weekly: loc.keep_weekly || '',
monthly: loc.keep_monthly || '',
yearly: loc.keep_yearly || ''
};
container.innerHTML = `
${toggleField}
${this.formRetention(`CFG_BACKUP_LOC_${idx}_`, values)}
`;
this.tagFieldsForSave(container);
},
async saveLocationModal() {
const modal = document.getElementById('backup-location-modal');
if (!modal) return;
const idx = parseInt(modal.dataset.locIdx, 10);
this.closeAllModals();
await this.saveSection(`location-${idx}`);
},
async deleteLocationModal() {
const modal = document.getElementById('backup-location-modal');
if (!modal) return;
const idx = parseInt(modal.dataset.locIdx, 10);
const loc = (this.locations?.locations || []).find(l => l.idx === idx);
const name = loc?.name || `Location ${idx}`;
if (!confirm(`Delete location "${name}"?\n\nBackup data already stored at this location is not deleted by this action — only LibrePortal's reference to it. The password file on disk also stays in place (rename it manually if you want to start fresh).`)) return;
this.closeAllModals();
await this.runTask(`libreportal backup location remove ${idx}`, 'backup', null);
setTimeout(() => this.reloadAfterSave(), 2000);
},
openAddLocationModal() {
const modal = document.getElementById('backup-add-location-modal');
const body = document.getElementById('backup-add-location-modal-body');
if (!modal || !body) return;
body.innerHTML = `
<div class="backup-form-grid">
${this.formSelect('__add_type', 'Type', 'local', [
['local', 'Local / mounted path'],
['sftp', 'SFTP'],
['rest', 'REST server'],
['s3', 'S3'],
['b2', 'Backblaze B2'],
['gs', 'Google Cloud Storage'],
['azure', 'Azure'],
['rclone', 'rclone']
])}
${this.formInput('__add_name', 'Friendly name', '', 'text', 'e.g. Office NAS')}
</div>
<p class="backup-card-hint" style="margin-top:12px">The location starts disabled — fill in its connection details on the next screen, then toggle Enabled.</p>
`;
modal.classList.add('open');
},
async confirmAddLocation() {
const modal = document.getElementById('backup-add-location-modal');
if (!modal) return;
const name = modal.querySelector('[name="__add_name"]')?.value?.trim();
const type = modal.querySelector('[name="__add_type"]')?.value || 'local';
if (!name) { this.notify('Name is required.', 'error'); return; }
this.closeAllModals();
const safeName = name.replace(/'/g, "'\\''");
await this.runTask(`libreportal backup location add '${safeName}' ${type}`, 'backup', null);
setTimeout(() => this.reloadAfterSave(), 2000);
},
});