diff --git a/public_html/admin/index.php b/public_html/admin/index.php index 0421920..6da58d0 100644 --- a/public_html/admin/index.php +++ b/public_html/admin/index.php @@ -4328,6 +4328,69 @@ async function clearanceRuleCreate() { } catch(e) { toast('Failed', 'err'); } } +// ── ARC REACTOR ────────────────────────────────────────────────────────────── +async function loadArc() { + const tbl = document.getElementById('arc-jobs-tbl'); + if (!tbl) return; + tbl.innerHTML = '
LOADING...
'; + + const status = document.getElementById('arc-job-filter')?.value || ''; + const [s, jobs] = await Promise.all([ + api('arc_status'), + api('arc_jobs', {status, limit: 100}), + ]); + + // status bar + const online = s?.online; + document.getElementById('arc-status-val').textContent = online ? '● ONLINE' : '○ OFFLINE'; + document.getElementById('arc-status-val').style.color = online ? 'var(--green)' : 'var(--red)'; + document.getElementById('arc-version-val').textContent = s?.version || '—'; + document.getElementById('arc-done-val').textContent = s?.jobs_done ?? s?.stats?.done ?? '—'; + document.getElementById('arc-fail-val').textContent = s?.jobs_failed ?? s?.stats?.failed ?? '—'; + document.getElementById('arc-hb-val').textContent = s?.last_heartbeat ? ts(s.last_heartbeat) : (online ? 'ALIVE' : '—'); + document.getElementById('arc-caps-val').textContent = Array.isArray(s?.capabilities) ? s.capabilities.join(' · ') : (s?.capabilities || '—'); + + const list = Array.isArray(jobs) ? jobs : (jobs?.jobs || []); + if (!list.length) { + tbl.innerHTML = '
No jobs found.
'; + return; + } + + const STATUS_COLOR = {queued:'var(--cyan)',running:'var(--yellow)',done:'var(--green)',failed:'var(--red)',cancelled:'var(--dim)'}; + const rows = list.map(j => { + const sc = STATUS_COLOR[j.status] || 'var(--text)'; + return ` + #${j.id} + ${esc(j.status||'').toUpperCase()} + ${esc(j.type||'—')} + ${esc(j.created_by||'—')} + ${j.result ? esc(JSON.stringify(j.result).substring(0,120)) : '—'} + ${ts(j.created_at)} + `; + }).join(''); + + tbl.innerHTML = ` + + ${rows}
IDSTATUSTYPEBYRESULTCREATED
`; +} + +async function arcTestPing() { + const d = await api('arc_ping'); + if (d?.job_id || d?.id) { + toast('Ping job queued — ID #' + (d.job_id || d.id), 'ok'); + setTimeout(loadArc, 1200); + } else { + toast('Ping failed: ' + (d?.error || 'no response'), 'err'); + } +} + +async function arcPurge() { + if (!confirm('Purge completed/failed jobs older than 24h?')) return; + const d = await api('arc_purge'); + toast(d?.purged != null ? `Purged ${d.purged} jobs` : 'Purge complete', 'ok'); + loadArc(); +} +