// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base. Object.assign(BackupPage.prototype, { async refreshAll() { const ts = Date.now(); const [dashboard, locations, , schema, migrate, peersData] = await Promise.all([ this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`), this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`), this.loadSystemConfigs(), this.fetchJson(`/data/backup/generated/schema.json?t=${ts}`), this.fetchJson(`/data/backup/generated/migrate.json?t=${ts}`), this.fetchJson(`/data/peers/generated/peers.json?t=${ts}`) ]); this.dashboard = dashboard; this.locations = locations; this.locSchema = schema; this.migrate = migrate; // Build hostname → friendly-name lookup once so renderMigrate can show // "homelab (host: homelab.lan)" instead of bare hostnames. this.hostnameToPeerName = {}; for (const p of (peersData?.peers || [])) { if (p.kind === 'backup-channel' && p.config?.hostname) { this.hostnameToPeerName[p.config.hostname] = p.name; } } this.snapshotsByLoc = {}; if (!this.engines.length) await this.loadEngines(); if (locations?.locations?.length) { const enabled = locations.locations.filter(l => l.enabled); await Promise.all(enabled.map(async (l) => { const s = await this.fetchJson(`/data/backup/generated/snapshots_${l.idx}.json?t=${ts}`); if (s) this.snapshotsByLoc[l.idx] = s; })); } }, async fetchJson(url) { try { const r = await fetch(url); if (!r.ok) return null; return await r.json(); } catch { return null; } }, async loadSystemConfigs() { const data = await this.fetchJson(`/data/config/generated/configs.json?t=${Date.now()}`); if (!data) return; window.configData = data; const flat = {}; for (const [k, v] of Object.entries(data.config || {})) flat[k] = v?.value ?? ''; window.systemConfigs = flat; }, async loadEngines() { const ts = Date.now(); const index = await this.fetchJson(`/data/backup/generated/engines/index.json?t=${ts}`); const ids = index?.engines || []; const metas = await Promise.all(ids.map(id => this.fetchJson(`/data/backup/generated/engines/${encodeURIComponent(id)}.json?t=${ts}`) )); this.engines = metas.filter(Boolean); // Fallback so the dropdown never collapses to empty if the regen // hasn't run yet — restic is always assumed available. if (!this.engines.length) { this.engines = [{ id: 'restic', name: 'Restic', supported_types: ['local','sftp','rest','s3','b2','gs','azure','rclone'] }]; } }, engineDisplayName(id) { if (!id) return 'Restic'; const match = (this.engines || []).find(e => e.id === id); return match?.name || id; }, enginesForType(type) { if (!type) return this.engines; return this.engines.filter(e => !Array.isArray(e.supported_types) || e.supported_types.includes(type) ); }, async reloadAfterSave() { await this.refreshAll(); this.render(); }, });