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

136 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base.
Object.assign(BackupPage.prototype, {
enhanceConfigurationWithPresets() {
this.enhanceEngineDetailsButton();
const lastInput = document.querySelector('#config-section [name="CFG_BACKUP_KEEP_LAST"]');
if (!lastInput) return;
const section = lastInput.closest('.config-category');
if (!section || section.dataset.backupPresetEnhanced === '1') return;
section.dataset.backupPresetEnhanced = '1';
const fieldNames = [
'CFG_BACKUP_KEEP_LAST',
'CFG_BACKUP_KEEP_DAILY',
'CFG_BACKUP_KEEP_WEEKLY',
'CFG_BACKUP_KEEP_MONTHLY',
'CFG_BACKUP_KEEP_YEARLY'
];
const inputs = fieldNames
.map(n => section.querySelector(`[name="${n}"]`))
.filter(Boolean);
if (inputs.length < 5) return;
const wrappers = inputs.map(input => {
return input.closest('.config-field, .field-group, .form-group') || input.parentElement;
});
const extraCustomFields = ['CFG_BACKUP_PRUNE_AFTER_FORGET'];
extraCustomFields.forEach(name => {
const el = section.querySelector(`[name="${name}"]`);
if (el) {
const wrap = el.closest('.config-field, .field-group, .form-group') || el.parentElement;
if (wrap) wrappers.push(wrap);
}
});
const readVals = () => ({
last: inputs[0].value || '',
daily: inputs[1].value || '',
weekly: inputs[2].value || '',
monthly: inputs[3].value || '',
yearly: inputs[4].value || ''
});
const preset = backupRetentionDetectPreset(readVals());
const meta = BACKUP_RETENTION_PRESET_META[preset];
const presetOptions = this.retentionPresetOptions(preset, false);
const block = document.createElement('div');
block.className = 'backup-retention-preset-block';
block.innerHTML = `
<label class="backup-form-row">
<span class="backup-form-label">Backup style <span class="tooltip" data-retention-tooltip title="${this.escape(meta?.hint || '')}"></span></span>
<select class="form-control" data-backup-retention-preset>${presetOptions}</select>
</label>
`;
const fieldsGrid = section.querySelector('.config-fields');
if (fieldsGrid) {
fieldsGrid.prepend(block);
} else {
section.prepend(block);
}
const applyVisibility = (presetKey) => {
const isCustom = presetKey === 'custom';
wrappers.forEach(w => { if (w) w.style.display = isCustom ? '' : 'none'; });
};
applyVisibility(preset);
const select = block.querySelector('[data-backup-retention-preset]');
const tooltipEl = block.querySelector('[data-retention-tooltip]');
select.addEventListener('change', () => {
const chosen = select.value;
if (tooltipEl) tooltipEl.title = BACKUP_RETENTION_PRESET_META[chosen]?.hint || '';
applyVisibility(chosen);
if (chosen === 'custom') return;
const p = BACKUP_RETENTION_PRESETS[chosen];
const map = { last: 0, daily: 1, weekly: 2, monthly: 3, yearly: 4 };
Object.entries(map).forEach(([k, i]) => {
inputs[i].value = p[k];
inputs[i].dispatchEvent(new Event('input', { bubbles: true }));
inputs[i].dispatchEvent(new Event('change', { bubbles: true }));
});
});
},
retentionPresetOptions(selected, includeInherit = false) {
const defaultKey = includeInherit ? 'inherit-global' : 'self-hosting';
const keys = Object.keys(BACKUP_RETENTION_PRESET_META)
.filter(k => k !== 'inherit-global' || includeInherit);
const ordered = [defaultKey, ...keys.filter(k => k !== defaultKey)];
return ordered.map(k => {
const base = BACKUP_RETENTION_PRESET_META[k].label;
const label = k === defaultKey ? `${base} (default)` : base;
return `<option value="${k}" ${k === selected ? 'selected' : ''}>${this.escape(label)}</option>`;
}).join('');
},
applyRetentionPreset(selectEl) {
const block = selectEl.closest('[data-retention-prefix]');
const advanced = block?.nextElementSibling;
if (!block) return;
const prefix = block.dataset.retentionPrefix;
const allowInherit = block.dataset.retentionAllowInherit === '1';
const preset = selectEl.value;
const tooltipEl = block.querySelector('[data-retention-tooltip]');
if (tooltipEl) tooltipEl.title = BACKUP_RETENTION_PRESET_META[preset]?.hint || '';
if (preset === 'custom') {
if (advanced) advanced.hidden = false;
} else {
if (advanced) advanced.hidden = true;
const p = BACKUP_RETENTION_PRESETS[preset];
if (p) {
const setField = (suffix, value) => {
const el = document.querySelector(`[name="${prefix}${suffix}"]`);
if (el) {
el.value = value;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
};
setField('KEEP_LAST', p.last);
setField('KEEP_DAILY', p.daily);
setField('KEEP_WEEKLY', p.weekly);
setField('KEEP_MONTHLY', p.monthly);
setField('KEEP_YEARLY', p.yearly);
}
}
// Keep CUSTOM_RETENTION in sync with the preset (location scope only).
if (allowInherit) {
const cr = block.querySelector(`[name="${prefix}CUSTOM_RETENTION"]`);
if (cr) cr.value = preset === 'inherit-global' ? 'false' : 'true';
}
},
});