Dashboard
Core…
Live
Initialising…
/* ═══════════════════════════════════════════════════════════════════════ INFRASTRUCTURE_COMMAND — Phase 2 + Phase 3 Phase 2: Controlled Actions (Add resources) Phase 3: Enterprise Delete (Governed destructive control) Constitution: Proposal → Audit → Confirm → Execute → Log ═══════════════════════════════════════════════════════════════════════ */ /* ── Standard audit modal (Phase 2 actions) ─────────────────────────── */ function infraModal(title, fields, onConfirm) { const existing = document.getElementById('infra-modal-overlay'); if (existing) existing.remove(); const overlay = document.createElement('div'); overlay.id = 'infra-modal-overlay'; overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:9999;'; const fieldsHtml = fields.map(f => `
${f.type==='select' ? `` : f.type==='checkbox' ? `` : `` }
`).join(''); overlay.innerHTML = `
${title}
All actions are audit-logged
${fieldsHtml}
`; document.body.appendChild(overlay); document.getElementById('imf-confirm-btn').onclick = () => { const reason = document.getElementById('imf-audit-reason').value.trim(); if (!reason) { document.getElementById('imf-audit-reason').style.borderColor='var(--red)'; return; } const values = { _reason: reason }; fields.forEach(f => { const el = document.getElementById(`imf-${f.key}`); if (el) values[f.key] = f.type==='checkbox' ? el.checked : el.value.trim(); }); for (const f of fields) { if (f.required && !values[f.key]) { const el = document.getElementById(`imf-${f.key}`); if (el) el.style.borderColor='var(--red)'; return; } } overlay.remove(); onConfirm(values); }; } /* ── Phase 3: Danger modal (type-to-confirm) ────────────────────────── */ function infraDangerModal(resourceType, name) { const existing = document.getElementById('infra-danger-overlay'); if (existing) existing.remove(); const timeLocks = { dns_zone:3600, dns_record:3600, email:3600, webspace:7200, database:86400 }; const delay = timeLocks[resourceType] || 3600; const delayStr = delay >= 3600 ? `${delay/3600}h` : `${delay/60}m`; const typeTarget = `DELETE ${name}`; const overlay = document.createElement('div'); overlay.id = 'infra-danger-overlay'; overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.85);display:flex;align-items:center;justify-content:center;z-index:9999;'; overlay.innerHTML = `
Request Deletion
Governed action. Execution delayed by ${delayStr}. Cancellable during grace period.
RESOURCE
${resourceType} / ${name}
`; document.body.appendChild(overlay); const btn = overlay.querySelector('#idm-confirm-btn'); overlay.querySelector('#idm-confirm').addEventListener('input', e => { const ok = e.target.value === typeTarget; btn.disabled = !ok; btn.style.opacity = ok ? '0.9' : '0.4'; }); btn.onclick = () => infraExecDelete(resourceType, name); } /* ── Action toast ───────────────────────────────────────────────────── */ async function infraExec(label, apiCall) { const toast = document.createElement('div'); toast.style.cssText = 'position:fixed;bottom:24px;right:24px;background:var(--bg2);border:1px solid var(--border2);border-radius:var(--radius2);padding:12px 18px;font-size:12px;color:var(--text);z-index:9998;font-family:var(--mono);'; toast.textContent = `⟳ ${label}…`; document.body.appendChild(toast); try { const result = await apiCall(); toast.style.borderColor = 'var(--green)'; toast.style.color = 'var(--green)'; toast.textContent = `✓ ${label}`; setTimeout(() => toast.remove(), 3000); return result; } catch(e) { toast.style.borderColor = 'var(--red)'; toast.style.color = 'var(--red)'; toast.textContent = `✗ ${label}: ${e.message}`; setTimeout(() => toast.remove(), 5000); throw e; } } async function infraExecDelete(resourceType, name) { const reason = document.getElementById('idm-reason')?.value?.trim(); const confirmInput = document.getElementById('idm-confirm')?.value?.trim(); if (!reason) { document.getElementById('idm-reason').style.borderColor='var(--red)'; return; } document.getElementById('infra-danger-overlay')?.remove(); try { const result = await infraExec(`Request deletion: ${name}`, () => Shell.api('/api/governance/delete-request', { method:'POST', body: JSON.stringify({ resource_type:resourceType, name, reason, confirm_input:confirmInput }) }) ); const notice = document.createElement('div'); notice.style.cssText = 'position:fixed;top:70px;right:24px;background:var(--bg2);border:1px solid var(--amber);border-radius:var(--radius2);padding:14px 18px;font-size:12px;z-index:9998;max-width:360px;'; const mins = Math.round((result.execute_in_seconds||3600)/60); notice.innerHTML = `
⏱ Deletion Scheduled
${name} — grace: ${mins}min
Cancel in Delete Queue.
`; document.body.appendChild(notice); setTimeout(() => notice.remove(), 8000); Modules.infrastructure(document.getElementById('content')); } catch(e) { const notice = document.createElement('div'); notice.style.cssText = 'position:fixed;top:70px;right:24px;background:var(--bg2);border:1px solid var(--red);border-radius:var(--radius2);padding:14px 18px;font-size:12px;z-index:9998;max-width:360px;'; notice.innerHTML = `
🔒 ${e.message.includes('POLICY')?'Policy Block':'Error'}
${e.message}
`; document.body.appendChild(notice); setTimeout(() => notice.remove(), 6000); } } async function infraSuspend(resourceType, name) { infraModal(`Suspend: ${name}`, [ { key:'reason', label:'Reason', placeholder:'Why suspend?', required:true }, ], async v => { await infraExec(`Suspend ${name}`, () => Shell.api(`/api/governance/suspend/${resourceType}/${name}`, { method:'POST', body:JSON.stringify({reason:v.reason}) }) ); Modules.infrastructure(document.getElementById('content')); }); } async function infraEnable(resourceType, name) { await infraExec(`Enable ${name}`, () => Shell.api(`/api/governance/enable/${resourceType}/${name}`, { method:'POST', body:JSON.stringify({reason:'Re-enabled via UI'}) }) ); Modules.infrastructure(document.getElementById('content')); } async function infraCancelDelete(resourceType, name) { if (!confirm(`Cancel deletion of ${name}? It will be restored to active.`)) return; await infraExec(`Cancel delete: ${name}`, () => Shell.api(`/api/governance/delete-cancel/${resourceType}/${name}`, { method:'POST', body:JSON.stringify({reason:'Cancelled via UI'}) }) ); Modules.infrastructure(document.getElementById('content')); } async function infraShowDeleteQueue() { const data = await Shell.api('/api/governance/delete-queue').catch(() => ({pending:[]})); const pending = data.pending || []; const now = Date.now()/1000; const overlay = document.createElement('div'); overlay.id = 'infra-queue-overlay'; overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.8);display:flex;align-items:center;justify-content:center;z-index:9999;'; const rows = pending.length===0 ? '
No pending deletions
' : pending.map(e => { const rem = Math.max(0, e.execute_at - now); const mins = Math.round(rem/60); return `
${e.name}
${e.resource_type} · ${e.requested_by} · ${e.reason}
${rem===0?'READY':'⏱ '+mins+'m'}
`; }).join(''); overlay.innerHTML = `
Delete Queue
${pending.length} pending · Time-lock active
${rows}
`; document.body.appendChild(overlay); } async function infraViewZone(domain) { const data = await Shell.api('/api/dns/zones/'+domain).catch(() => ({records:[]})); const overlay = document.createElement('div'); overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.8);display:flex;align-items:center;justify-content:center;z-index:9999;'; overlay.innerHTML = `
Zone: ${domain}
${(data.records||[]).join('\n')}
`; document.body.appendChild(overlay); } /* ── Add actions ─────────────────────────────────────────────────────── */ function infraAddZone() { infraModal('Add DNS Zone', [ { key:'domain', label:'Domain', placeholder:'example.com', required:true }, { key:'ip', label:'IP Address', placeholder:'192.99.7.223', required:true, default:'192.99.7.223' }, ], async v => { await infraExec(`Add zone ${v.domain}`, () => Shell.api('/api/dns/zones/'+v.domain, { method:'POST', body:JSON.stringify({ip:v.ip}) }) ); Modules.infrastructure(document.getElementById('content')); }); } function infraAddRecord(domain) { infraModal(`Add Record — ${domain}`, [ { key:'name', label:'Name', placeholder:'@ or subdomain', required:true, default:'@' }, { key:'type', label:'Type', type:'select', options:['A','AAAA','CNAME','MX','TXT','NS'], required:true }, { key:'value', label:'Value', placeholder:'IP or target', required:true }, { key:'ttl', label:'TTL', placeholder:'900', default:'900' }, ], async v => { await infraExec(`Add ${v.type} record`, () => Shell.api(`/api/dns/zones/${domain}/records`, { method:'POST', body:JSON.stringify({name:v.name, type:v.type, value:v.value, ttl:parseInt(v.ttl)||900}) }) ); Modules.infrastructure(document.getElementById('content')); }); } function infraAddMailbox() { infraModal('Add Mailbox', [ { key:'username', label:'Username', placeholder:'john', required:true }, { key:'domain', label:'Domain', default:'3enet.ca', required:true }, { key:'password', label:'Password', type:'password', placeholder:'Strong password', required:true }, ], async v => { await infraExec(`Create ${v.username}@${v.domain}`, () => Shell.api('/api/email/mailboxes', { method:'POST', body:JSON.stringify({username:v.username, password:v.password, domain:v.domain}) }) ); Modules.infrastructure(document.getElementById('content')); }); } function infraAddDatabase() { infraModal('Add Database', [ { key:'name', label:'Database Name', placeholder:'myapp_db', required:true }, { key:'owner', label:'Owner', placeholder:'myapp_user', required:true }, { key:'password', label:'Password', type:'password', placeholder:'Strong password', required:true }, ], async v => { await infraExec(`Create DB ${v.name}`, () => Shell.api('/api/db/databases', { method:'POST', body:JSON.stringify({name:v.name, owner:v.owner, password:v.password}) }) ); Modules.infrastructure(document.getElementById('content')); }); } function infraAddSite() { infraModal('Add Web Site', [ { key:'domain', label:'Domain', placeholder:'mysite.3enet.ca', required:true }, { key:'ssl', label:'SSL', type:'checkbox', checkLabel:"Enable SSL (Let's Encrypt)", default:true }, { key:'php', label:'PHP', type:'checkbox', checkLabel:'Enable PHP 8.3' }, ], async v => { await infraExec(`Create site ${v.domain}`, () => Shell.api('/api/webspace/sites/'+v.domain, { method:'POST', body:JSON.stringify({ssl:v.ssl, php:v.php}) }) ); Modules.infrastructure(document.getElementById('content')); }); } function infraRenewSSL(domain) { infraModal(`Renew SSL: ${domain}`, [], async v => { await infraExec(`Renew SSL ${domain}`, () => Shell.api(`/api/webspace/ssl/${domain}/renew`, { method:'POST' }) ); Modules.infrastructure(document.getElementById('content')); }); } /* ── Main module ─────────────────────────────────────────────────────── */ Modules.infrastructure = async (el) => { el.innerHTML = `
Infrastructure Command
PHASE 3 · ENTERPRISE GOVERNANCE · Proposal → Audit → Confirm → Execute → Log
Loading…
`; const body = el.querySelector('#infra-body'); try { const [zones, mailboxes, databases, sites, certs, govStates, deleteQueue] = await Promise.all([ Shell.api('/api/dns/zones').catch(() => ({zones:[]})), Shell.api('/api/email/mailboxes').catch(() => ({mailboxes:[]})), Shell.api('/api/db/databases').catch(() => ({databases:[]})), Shell.api('/api/webspace/sites').catch(() => ({sites:[]})), Shell.api('/api/webspace/ssl').catch(() => ({certs:[]})), Shell.api('/api/governance/states').catch(() => ({})), Shell.api('/api/governance/delete-queue').catch(() => ({pending:[]})), ]); const now = Date.now(); const soonMs = 30*24*60*60*1000; const certMap = {}; (certs.certs||[]).forEach(c => { const diff = new Date(c.expires) - now; certMap[c.domain] = { expires:c.expires, status: diff<0?'expired':diff { pendingMap[e.name] = e; }); const getState = (type, name) => pendingMap[name] ? 'pending_delete' : (govStates[type]?.[name] || 'active'); const expiredC = (certs.certs||[]).filter(c => certMap[c.domain]?.status==='expired'); const expiringC = (certs.certs||[]).filter(c => certMap[c.domain]?.status==='expiring'); const cleanMail = (mailboxes.mailboxes||[]).filter(m => !['nobody','systemd-network','systemd-timesync','systemd-resolve','fwupd-refresh','polkitd'].includes(m)); /* ── action buttons helper ── */ const actionBtns = (type, name) => { const st = getState(type, name); const rem = pendingMap[name] ? Math.max(0, pendingMap[name].execute_at - now/1000) : 0; if (st==='pending_delete') return ` ⏱ ${Math.round(rem/60)}m `; if (st==='suspended') return ` SUSPENDED `; return ` `; }; const stateDot = (type, name) => { const st = getState(type, name); return st==='pending_delete'?'var(--red)':st==='suspended'?'var(--amber)':''; }; body.innerHTML = `
${[ ['DNS Zones', zones.zones?.length||0, 'var(--accent)'], ['Mailboxes', cleanMail.length, 'var(--green)'], ['Databases', databases.databases?.length||0, 'var(--purple)'], ['Web Sites', sites.sites?.length||0, 'var(--amber)'], ['SSL Certs', certs.certs?.length||0, expiredC.length?'var(--red)':expiringC.length?'var(--amber)':'var(--green)'], ].map(([l,v,c]) => `
${l}
${v}
`).join('')}
${(expiredC.length||expiringC.length)?`
⚠ Audit-Driven Recommendations
${[...expiredC,...expiringC].map(c=>`
${c.domain} ${certMap[c.domain].status==='expired'?'SSL EXPIRED':'expiring '+c.expires}
`).join('')}
`:''}
◈ DNS Zones
${zones.zones?.length||0} · threeEdnsA→B
${(zones.zones||[]).map(z=>{ const dot = stateDot('dns_zone',z)||'var(--green)'; const st = getState('dns_zone',z); return `
${z} ${st==='active'?` `:''} ${actionBtns('dns_zone',z)}
`; }).join('')}
◈ Email Mailboxes
${cleanMail.length} · mail.3enet.ca
${cleanMail.map(m=>{ const dot = stateDot('email',m)||'var(--accent)'; const st = getState('email',m); return `
${m}@3enet.ca ${actionBtns('email',m)}
`; }).join('')}
◈ Databases
${databases.databases?.length||0} · PostgreSQL
${(databases.databases||[]).map(d=>{ const dot = stateDot('database',d)||'var(--purple)'; const st = getState('database',d); return `
${d} PostgreSQL ${actionBtns('database',d)}
`; }).join('')}
◈ Web Sites
${sites.sites?.length||0} · nginx
${(sites.sites||[]).map(s=>{ const cert=certMap[s]; const dot=!cert?'var(--text3)':cert.status==='expired'?'var(--red)':cert.status==='expiring'?'var(--amber)':'var(--green)'; const ssl=!cert?'no SSL':cert.status==='expired'?'SSL EXPIRED':cert.status==='expiring'?'expiring':'SSL ✓'; return `
${s} ${ssl} ${actionBtns('webspace',s)}
`; }).join('')}
◈ SSL Certificates
${(certs.certs||[]).map(c=>{ const st=certMap[c.domain]?.status||'ok'; const dot=st==='expired'?'var(--red)':st==='expiring'?'var(--amber)':'var(--green)'; return `
${c.domain} ${c.expires} ${st.toUpperCase()}
`; }).join('')}
PHASE 3 · ENTERPRISE GOVERNANCE · All destructive actions are time-locked and audited
`; } catch(e) { body.innerHTML = `
Failed: ${e.message}
`; } };