← back to Ventura Corridor
iter 161: /health.html — pipeline-wide one-page status w/ pipeline+top-tapped+growth panels, refresh 30s
b39e8cf520ba3f036aef9da2775c3ab637745b4f · 2026-05-06 19:41:36 -0700 · SteveStudio2
Files touched
A public/health.htmlM public/today.htmlM src/server/index.ts
Diff
commit b39e8cf520ba3f036aef9da2775c3ab637745b4f
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 6 19:41:36 2026 -0700
iter 161: /health.html — pipeline-wide one-page status w/ pipeline+top-tapped+growth panels, refresh 30s
---
public/health.html | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++
public/today.html | 1 +
src/server/index.ts | 3 +-
3 files changed, 149 insertions(+), 1 deletion(-)
diff --git a/public/health.html b/public/health.html
new file mode 100644
index 0000000..ef086c0
--- /dev/null
+++ b/public/health.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Health · The Corridor</title>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;1,400;1,500&family=Inter:wght@200;300;400;500&family=JetBrains+Mono:wght@200;300&display=swap');
+:root{--noir:#0a0a0c;--noir-rise:#131316;--ink:#f0ece2;--ink-mute:#888475;--metal:#b89968;--metal-glow:#d4b683;--green:#6a9b73;--red:#b66565;--yellow:#c9a942;--rule:#2a2622}
+*{box-sizing:border-box}body{margin:0;background:var(--noir);color:var(--ink);font-family:Inter,sans-serif;font-weight:300}
+header{padding:30px 32px 16px;border-bottom:1px solid var(--rule)}
+header h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:400;font-size:32px;margin:0}
+header h1 em{color:var(--metal)}
+header .sub{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--ink-mute);margin-top:4px}
+nav{padding:10px 32px;border-bottom:1px solid var(--rule);display:flex;gap:12px;flex-wrap:wrap;font-size:9px;letter-spacing:.26em;text-transform:uppercase}
+nav a{color:var(--ink-mute);text-decoration:none;padding:5px 10px;border:1px solid var(--rule)}
+nav a:hover, nav a.active{color:var(--metal);border-color:var(--metal)}
+
+main{padding:32px;max-width:1200px;margin:0 auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(360px,1fr));gap:24px}
+.panel{background:var(--noir-rise);border:1px solid var(--rule);padding:24px}
+.panel.ok{border-left:3px solid var(--green)}
+.panel.warn{border-left:3px solid var(--yellow)}
+.panel.err{border-left:3px solid var(--red)}
+.panel h3{font-family:'Cormorant Garamond',serif;font-style:italic;color:var(--metal);font-size:24px;margin:0 0 4px;font-weight:500}
+.panel .what{font-size:9px;letter-spacing:.32em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:14px}
+.row{display:grid;grid-template-columns:1fr auto;gap:14px;padding:6px 0;border-bottom:1px dotted var(--rule);font-size:13px;font-family:'JetBrains Mono',monospace;align-items:baseline}
+.row .k{color:var(--ink)}
+.row .v{color:var(--metal-glow);font-weight:500}
+.row .v.ok::before{content:'✓ ';color:var(--green)}
+.row .v.warn::before{content:'⚠ ';color:var(--yellow)}
+.row .v.err::before{content:'✕ ';color:var(--red)}
+.summary{grid-column:1/-1;text-align:center;padding:18px;background:var(--noir-rise);border:1px solid var(--rule);font-family:'Cormorant Garamond',serif;font-style:italic;font-size:24px}
+.summary.ok{color:var(--green)}
+.summary.warn{color:var(--yellow)}
+.summary.err{color:var(--red)}
+.last-up{font-size:11px;color:var(--ink-mute);font-family:'JetBrains Mono',monospace;text-align:right;padding:0 32px 24px;font-style:normal}
+</style>
+</head>
+<body>
+<header>
+ <h1>Health <em>check</em></h1>
+ <div class="sub">whole-pipeline status · refreshed every 30s</div>
+</header>
+<nav>
+ <a href="/today.html">today</a>
+ <a href="/magazine.html">magazine</a>
+ <a href="/issue">issue</a>
+ <a href="/coverage.html">coverage</a>
+ <a href="/health.html" class="active">health</a>
+</nav>
+<main id="root"><div class="summary">Loading…</div></main>
+<div class="last-up" id="last-up"></div>
+<script>
+function fmt(n) { return Number(n).toLocaleString('en-US'); }
+function escHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); }
+
+async function load() {
+ let q, c, h, gen, sb, fb, inq;
+ try {
+ [q, c, h, gen, sb, fb, inq] = await Promise.all([
+ fetch('/api/magazine/quality-flags').then(r => r.json()),
+ fetch('/api/coverage').then(r => r.json()),
+ fetch('/api/coverage/history').then(r => r.json()),
+ fetch('/api/magazine/gen-queue?limit=1').then(r => r.json()),
+ fetch('/api/magazine/scoreboard').then(r => r.json()),
+ fetch('/api/feedback').then(r => r.json()),
+ fetch('/api/sponsor/inquiries').then(r => r.json()),
+ ]);
+ } catch (e) {
+ document.getElementById('root').innerHTML = `<div class="summary err">cannot reach API: ${escHtml(e.message)}</div>`;
+ return;
+ }
+
+ const lastSnap = (h.snapshots || []).at(-1);
+ const sinceLastSnap = lastSnap ? ((Date.now() - new Date(lastSnap.taken_at).getTime()) / 3600000) : null;
+ const fbUnread = (fb.rows || []).filter(r => !r.reviewed).length;
+ const inqOpen = (inq.rows || []).filter(r => r.status === 'new').length;
+ const queueDepth = (gen.queue || []).length === 0 ? 'empty' : `${(gen.queue || []).length}+ waiting`;
+
+ const checks = [
+ { ok: c.totals.published >= 1, k: 'Published features', v: fmt(c.totals.published) },
+ { ok: c.totals.draft >= 0, k: 'Draft features', v: fmt(c.totals.draft) },
+ { ok: c.totals.reviewed >= 0, k: 'Reviewed (awaiting publish)', v: fmt(c.totals.reviewed) },
+ { ok: q.count === 0, k: 'Quality flags', v: q.count === 0 ? 'none' : `${q.count} issues`, status: q.count === 0 ? 'ok' : 'warn' },
+ { ok: c.totals.total_biz >= 1000, k: 'Businesses indexed', v: fmt(c.totals.total_biz) },
+ { ok: queueDepth !== 'empty', k: 'Gen queue', v: queueDepth, status: queueDepth === 'empty' ? 'warn' : 'ok' },
+ { ok: lastSnap && sinceLastSnap < 26, k: 'Last coverage snapshot', v: sinceLastSnap == null ? 'never' : `${sinceLastSnap.toFixed(1)}h ago`, status: sinceLastSnap == null ? 'warn' : sinceLastSnap < 26 ? 'ok' : 'warn' },
+ { ok: fbUnread === 0, k: 'Reader feedback unread', v: fbUnread === 0 ? '0' : `${fbUnread} unread`, status: fbUnread === 0 ? 'ok' : 'warn' },
+ { ok: inqOpen === 0, k: 'Sponsor inquiries open', v: inqOpen === 0 ? '0' : `${inqOpen} open`, status: inqOpen === 0 ? 'ok' : 'warn' },
+ ];
+
+ const overall = checks.every(x => (x.status || (x.ok ? 'ok' : 'err')) === 'ok')
+ ? 'ok' : checks.some(x => (x.status || (x.ok ? 'ok' : 'err')) === 'err') ? 'err' : 'warn';
+ const overallText = overall === 'ok' ? 'All systems normal.' : overall === 'warn' ? 'Some attention items.' : 'Something is broken.';
+
+ let html = `<div class="summary ${overall}">${escHtml(overallText)}</div>`;
+ html += `<div class="panel ${overall}">
+ <h3>Pipeline</h3>
+ <div class="what">core counts · queues · flags</div>
+ ${checks.map(c => {
+ const status = c.status || (c.ok ? 'ok' : 'err');
+ return `<div class="row"><span class="k">${escHtml(c.k)}</span><span class="v ${status}">${escHtml(c.v)}</span></div>`;
+ }).join('')}
+ </div>`;
+
+ // Top boards
+ const top = (sb.top_tapped || []).slice(0, 5);
+ html += `<div class="panel ok">
+ <h3>Top tapped this week</h3>
+ <div class="what">most outbound clicks</div>
+ ${top.length === 0 ? '<div style="font-style:italic;color:var(--ink-mute);font-family:Cormorant Garamond,serif">no taps yet</div>' :
+ top.map(t => `<div class="row"><span class="k"><a href="/magazine/${t.id}" style="color:var(--ink);text-decoration:none">${escHtml((t.headline || '').slice(0, 40))}</a></span><span class="v">${t.taps} taps</span></div>`).join('')}
+ </div>`;
+
+ // Coverage growth
+ const snaps = h.snapshots || [];
+ if (snaps.length >= 2) {
+ const first = snaps[0], last = snaps[snaps.length - 1];
+ const days = (new Date(last.taken_at) - new Date(first.taken_at)) / 86400000;
+ const grew = Number(last.total_features) - Number(first.total_features);
+ const rate = days > 0 ? (grew / days).toFixed(1) : '—';
+ html += `<div class="panel ok">
+ <h3>Growth</h3>
+ <div class="what">since first snapshot</div>
+ <div class="row"><span class="k">Days observed</span><span class="v">${days.toFixed(1)}</span></div>
+ <div class="row"><span class="k">Features added</span><span class="v">+${grew}</span></div>
+ <div class="row"><span class="k">Avg per day</span><span class="v">${rate}</span></div>
+ <div class="row"><span class="k">Snapshots taken</span><span class="v">${snaps.length}</span></div>
+ </div>`;
+ } else {
+ html += `<div class="panel warn">
+ <h3>Growth</h3>
+ <div class="what">need ≥2 snapshots for trend</div>
+ <div class="row"><span class="k">Snapshots taken</span><span class="v">${snaps.length}</span></div>
+ <div class="row"><span class="k">Next snapshot</span><span class="v">11:55 PM</span></div>
+ </div>`;
+ }
+
+ document.getElementById('root').innerHTML = html;
+ document.getElementById('last-up').textContent = `last refresh ${new Date().toLocaleTimeString()}`;
+}
+load();
+setInterval(load, 30_000);
+</script>
+</body>
+</html>
diff --git a/public/today.html b/public/today.html
index def2c6f..390daa1 100644
--- a/public/today.html
+++ b/public/today.html
@@ -126,6 +126,7 @@
<a href="/cover-picker.html">⛧ cover</a>
<a href="/coverage.html">▰ coverage</a>
<a href="/magazine.html?quality=1" id="nav-quality" style="color:var(--ink-mute)">🔧 quality</a>
+ <a href="/health.html">♥ health</a>
<a href="/crawl-derby.html">🏇 derby</a>
<a href="/today.html" class="active">today</a>
<span class="theme-toggle-host" style="display:inline-flex;align-items:center;margin-left:6px"></span>
diff --git a/src/server/index.ts b/src/server/index.ts
index 4756dc0..c785032 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -85,7 +85,8 @@ const ADMIN_PATHS = [
/^\/words\/?$/i,
/^\/quotes\/?$/i,
/^\/coverage(\.html)?\/?$/i,
- /^\/api\/coverage\/?$/i,
+ /^\/api\/coverage(\/.*)?$/i,
+ /^\/health(\.html)?\/?$/i,
/^\/sitemap\.xml\/?$/i,
/^\/robots\.txt\/?$/i,
/^\/issue(\/[\w-]+)?\/feed\.xml\/?$/i,
← 4fc18b8 iter 160: /issue.txt — plain-text dump of full issue (50KB,
·
back to Ventura Corridor
·
iter 162: /api/magazine/:id/social-pack.json — programmatic fcadf64 →