Some checks are pending
offline / test (push) Waiting to run
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.
76 lines
3.9 KiB
HTML
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>
|