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>
126 lines
5.9 KiB
JavaScript
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);
|
|
},
|
|
});
|