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>
85 lines
4.3 KiB
JavaScript
85 lines
4.3 KiB
JavaScript
// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base.
|
|
Object.assign(BackupPage.prototype, {
|
|
enhanceEngineDetailsButton() {
|
|
const selector = '[name="CFG_BACKUP_ENGINE"], [name^="CFG_BACKUP_LOC_"][name$="_ENGINE"]';
|
|
document.querySelectorAll(`#config-section ${selector}, .backup-location-details ${selector}`).forEach((engineInput) => {
|
|
const customSelect = engineInput.closest('.custom-select');
|
|
const wrapTarget = customSelect || engineInput;
|
|
const group = wrapTarget.closest('.field-group') || wrapTarget.parentElement;
|
|
if (!group || group.dataset.engineDetailsBound === '1') return;
|
|
group.dataset.engineDetailsBound = '1';
|
|
|
|
const btn = document.createElement('button');
|
|
btn.type = 'button';
|
|
btn.className = 'backup-secondary-btn backup-engine-details-btn';
|
|
btn.dataset.action = 'open-engine-details';
|
|
btn.innerHTML = `
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
<line x1="12" y1="16" x2="12" y2="12"></line>
|
|
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
|
</svg>
|
|
Details
|
|
`;
|
|
|
|
const wrap = document.createElement('div');
|
|
wrap.className = 'backup-engine-input-row';
|
|
wrapTarget.parentNode.insertBefore(wrap, wrapTarget);
|
|
wrap.appendChild(wrapTarget);
|
|
wrap.appendChild(btn);
|
|
});
|
|
},
|
|
async openEngineDetailsModal(triggerEl) {
|
|
const modal = document.getElementById('backup-engine-modal');
|
|
const body = document.getElementById('backup-engine-modal-body');
|
|
const title = document.getElementById('backup-engine-modal-title');
|
|
if (!modal || !body) return;
|
|
|
|
// Find the engine select adjacent to the Details button that fired
|
|
// this event so per-location Details work even when the user has
|
|
// changed the select but not saved yet.
|
|
let engineId = (window.systemConfigs?.CFG_BACKUP_ENGINE || 'restic').trim();
|
|
const row = triggerEl?.closest('.backup-engine-input-row');
|
|
const sel = row?.querySelector('select, input');
|
|
if (sel && sel.value) engineId = sel.value.trim();
|
|
body.innerHTML = `<div class="backup-empty-state">Loading engine details…</div>`;
|
|
modal.classList.add('open');
|
|
|
|
const data = await this.fetchJson(`/data/backup/generated/engines/${encodeURIComponent(engineId)}.json?t=${Date.now()}`);
|
|
if (!data) {
|
|
body.innerHTML = `
|
|
<div class="backup-empty-state">
|
|
No details file for engine "<strong>${this.escape(engineId)}</strong>".<br>
|
|
Add <code>scripts/backup/engines/${this.escape(engineId)}.json</code> and run the WebUI regen.
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
if (title) title.textContent = `Backup engine: ${data.name || engineId}`;
|
|
const propsHTML = (data.properties || []).map(p =>
|
|
`<tr><th>${this.escape(p.label)}</th><td>${this.escape(p.value)}</td></tr>`
|
|
).join('');
|
|
const featsHTML = (data.features || []).map(f => `<li>${this.escape(f)}</li>`).join('');
|
|
const docsHTML = data.docs_url
|
|
? `<a href="${this.escape(data.docs_url)}" target="_blank" rel="noopener noreferrer" class="backup-engine-docs-link">${this.escape(data.docs_url)} ↗</a>`
|
|
: '';
|
|
const logoHTML = data.logo
|
|
? `<img class="backup-engine-logo" src="${this.escape(data.logo)}" alt="" onerror="this.style.display='none'">`
|
|
: '';
|
|
|
|
body.innerHTML = `
|
|
<div class="backup-engine-modal-head">
|
|
${logoHTML}
|
|
<div>
|
|
<h4>${this.escape(data.name || engineId)}</h4>
|
|
<p class="backup-card-hint">${this.escape(data.tagline || '')}</p>
|
|
</div>
|
|
</div>
|
|
${propsHTML ? `<table class="backup-engine-props">${propsHTML}</table>` : ''}
|
|
${featsHTML ? `<h5>Highlights</h5><ul class="backup-engine-features">${featsHTML}</ul>` : ''}
|
|
${docsHTML ? `<h5>Documentation</h5><p>${docsHTML}</p>` : ''}
|
|
`;
|
|
},
|
|
});
|