master-zapier-plan-draft/packages/verae-fleet/public/index.html
George Lambert dd99ce1c64
Some checks are pending
offline / test (push) Waiting to run
Add fleet control: service configs, replica floors, monitor, pause/restart
Central fleet.json sets min/max copies. Tree-node keepFloor respawns
until three healthy unpaused replicas remain. CLI and loopback UI
pause, resume, stop, and restart instances that fail health checks.
2026-09-11 13:09:55 -04:00

76 lines
3.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Verae fleet monitor</title>
<style>
:root { --ink:#12202c; --muted:#5b6d78; --line:#d5dee4; --bg:#f3efe8; --ok:#0f6e56; --err:#a32020; --warn:#8a5a00; }
* { box-sizing: border-box; }
body { margin:0; font:14px/1.45 system-ui,sans-serif; color:var(--ink); background:var(--bg); }
header { background:var(--ink); color:#f6f3ee; padding:.9rem 1.1rem; }
header h1 { margin:0; font-size:1.1rem; }
header p { margin:.35rem 0 0; color:#c5d0d8; font-size:13px; }
main { padding:1rem; max-width:1100px; margin:0 auto; }
table { width:100%; border-collapse:collapse; background:#fff; }
th, td { text-align:left; padding:.4rem .5rem; border-bottom:1px solid var(--line); font-size:13px; }
th { font-size:11px; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.pill { font:700 10px system-ui; letter-spacing:.06em; text-transform:uppercase; padding:.12rem .35rem; border-radius:4px; }
.good { background:#d4efe6; color:var(--ok); }
.bad { background:#f8d4d4; color:var(--err); }
.warn { background:#f5e6c8; color:var(--warn); }
button { margin-right:.25rem; font:650 12px system-ui; border:1px solid var(--line); background:#fff; border-radius:5px; padding:.25rem .45rem; cursor:pointer; }
code { font:12px ui-monospace,Menlo,monospace; }
.inst { font:12px ui-monospace,Menlo,monospace; margin:.15rem 0; }
</style>
</head>
<body>
<header>
<h1>Verae fleet monitor</h1>
<p>Active replicas, replica floors, pause / restart. Tree-node <code>keepFloor</code> respawns until <code>min</code> copies are healthy and unpaused. Loopback only.</p>
</header>
<main>
<p id="meta"></p>
<table>
<thead><tr><th>Service</th><th>Config</th><th>min/max</th><th>available</th><th>instances</th><th>actions</th></tr></thead>
<tbody id="rows"></tbody>
</table>
</main>
<script>
async function j(url, opts) { const r = await fetch(url, opts); return r.json(); }
async function act(path) { await j(path, { method:'POST' }); await draw(); }
async function draw() {
const s = await j('/api/status');
document.getElementById('meta').textContent = 'probe ' + (s.monitor?.t || '—') + ' · NATS ' + (s.nats?.url || '');
const tb = document.getElementById('rows');
tb.innerHTML = Object.values(s.services).map((sv) => {
const floor = sv.belowFloor ? '<span class="pill bad">below floor</span>' : (sv.keepFloor ? '<span class="pill good">floor ok</span>' : '');
const inst = (sv.instances || []).map((i) => {
const st = !i.pid ? 'dead' : i.paused ? 'paused' : i.healthy === false ? 'unhealthy' : 'up';
const cls = st === 'up' ? 'good' : st === 'paused' ? 'warn' : 'bad';
return `<div class="inst"><span class="pill ${cls}">${st}</span> ${i.id} pid=${i.pid || '—'} :${i.healthPort}
<button onclick="act('/api/instances/${i.id}/pause')">pause</button>
<button onclick="act('/api/instances/${i.id}/resume')">resume</button>
<button onclick="act('/api/instances/${i.id}/restart')">restart</button>
</div>`;
}).join('') || '<span style="color:#5b6d78">none</span>';
return `<tr>
<td><strong>${sv.id}</strong><br><span style="color:#5b6d78">${sv.title || ''}</span></td>
<td><code>${sv.configPath || ''}</code></td>
<td>${sv.min} / ${sv.max} ${floor}</td>
<td>${sv.available} running=${sv.running} paused=${sv.paused}</td>
<td>${inst}</td>
<td>
<button onclick="act('/api/services/${sv.id}/start')">on</button>
<button onclick="act('/api/services/${sv.id}/pause')">pause</button>
<button onclick="act('/api/services/${sv.id}/resume')">resume</button>
<button onclick="act('/api/services/${sv.id}/stop')">off</button>
</td>
</tr>`;
}).join('');
}
draw();
setInterval(draw, 1500);
</script>
</body>
</html>