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