// Disk Safety Settings Panel (function() { let diskSettings = null; async function loadDiskSettings() { try { const res = await secureFetch('/api/v1/disk-settings'); if (res.ok) { diskSettings = await res.json(); renderDiskSettingsModal(); } } catch (e) { console.error('Failed to load disk settings:', e); } } function renderDiskSettingsModal() { const existing = document.getElementById('disk-settings-modal'); if (existing) existing.remove(); const c = diskSettings?.current || {}; const du = diskSettings?.diskUsage || {}; const usedGB = (du.dataDirSize / 1073741824).toFixed(2); const diskFreeGB = (du.free / 1073741824).toFixed(1); const diskTotalGB = (du.total / 1073741824).toFixed(1); const diskPct = du.total > 0 ? ((du.used / du.total) * 100).toFixed(1) : 0; const modal = document.createElement('div'); modal.id = 'disk-settings-modal'; modal.className = 'modal'; modal.style.cssText = 'display:flex;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;align-items:center;justify-content:center;'; modal.innerHTML = `

💾 Disk Safety

DashCaddy Data Size ${usedGB} GB
Disk Free ${diskFreeGB} GB / ${diskTotalGB} GB (${diskPct}% used)
⚠ Disk impact: Lowering the health check interval increases how often data is written to disk. DashCaddy caps history at max entries per service and prunes entries older than the retention period, so these two values together determine steady-state disk usage. For busy hosts, prefer a longer interval (60–120s) and a lower entry cap.
${settingRow('Health Check Interval', 'healthInterval', c.healthCheckInterval, 'seconds', Math.round((c.healthCheckInterval||30000)/1000), 5, 300)} ${settingRow('Max Health Entries/Service', 'healthMaxEntries', c.healthMaxEntries, 'entries', c.healthMaxEntries||500, 50, 5000)} ${settingRow('Health Retention', 'healthRetentionDays', c.healthRetentionDays, 'days', c.healthRetentionDays||30, 1, 90)} ${settingRow('Max Stats Entries', 'statsMaxEntries', c.statsMaxEntries, 'entries', c.statsMaxEntries||500, 100, 10000)} ${settingRow('Max Audit Entries', 'auditMaxEntries', c.auditMaxEntries, 'entries', c.auditMaxEntries||1000, 100, 10000)}

Some changes apply on next container restart.

`; document.body.appendChild(modal); // Audit P0-3 (2026-10-27): the meta CSP blocks inline event handlers, so // the injected onclick/oninput attributes were dead — Save/Clean Up/ // Close did nothing and slider value labels never updated. Wire after // injection instead. document.getElementById('disk-close-btn').addEventListener('click', () => modal.remove()); document.getElementById('disk-save-btn').addEventListener('click', () => saveDiskSettings()); document.getElementById('disk-cleanup-btn').addEventListener('click', () => cleanupDiskNow()); modal.querySelectorAll('input[type="range"]').forEach((inp) => { inp.addEventListener('input', () => { const out = document.getElementById(`${inp.id}-val`); if (out) out.textContent = inp.value; }); }); } function settingRow(label, id, current, unit, displayVal, min, max) { return `
${displayVal} ${unit}
`; } async function saveDiskSettings() { const payload = { healthInterval: parseInt(document.getElementById('disk-healthInterval').value) * 1000, healthMaxEntries: parseInt(document.getElementById('disk-healthMaxEntries').value), healthRetentionDays: parseInt(document.getElementById('disk-healthRetentionDays').value), statsMaxEntries: parseInt(document.getElementById('disk-statsMaxEntries').value), auditMaxEntries: parseInt(document.getElementById('disk-auditMaxEntries').value), }; try { const res = await secureFetch('/api/v1/disk-settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (data.success) { showToast('Disk settings saved', 'success'); } else { showToast(data.error || 'Save failed', 'error'); } } catch (e) { showToast('Error: ' + e.message, 'error'); } } async function cleanupDiskNow() { if (!confirm('Clean up old health entries, stats, and audit logs now?')) return; try { const res = await secureFetch('/api/v1/disk-settings/cleanup', { method: 'POST' }); const data = await res.json(); if (data.success) { const items = Object.entries(data.results.cleaned).map(([k,v]) => `${k}: ${v}`).join('\n'); showToast('Cleanup complete', 'success'); loadDiskSettings(); } } catch (e) { showToast('Error: ' + e.message, 'error'); } } window.openDiskSettings = function() { loadDiskSettings(); }; window.saveDiskSettings = saveDiskSettings; window.cleanupDiskNow = cleanupDiskNow; // Audit P0-3: index.html's Disk button carried an inline handler calling // openDiskSettings() — dead under the meta CSP. Wired here instead. const diskOpenBtn = document.getElementById('disk-settings-open'); if (diskOpenBtn) diskOpenBtn.addEventListener('click', () => loadDiskSettings()); })();