Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

status.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

status.tsxBlame360 lines · 2 contributors
2316be6Claude1/**
2 * Public /status — human-readable platform health dashboard.
3 *
4 * Unlike /healthz (LB liveness JSON) and /readyz (DB readiness JSON),
5 * /status renders a full HTML page anyone can load. Shows DB reachability,
6 * autopilot state, totals (users/repos/gate runs), and the most recent
7 * autopilot tick's task breakdown.
8 *
9 * Accessible without auth. Uses softAuth so the nav bar renders correctly
10 * for logged-in visitors.
11 */
12
13import { Hono } from "hono";
14import { sql } from "drizzle-orm";
15import { db } from "../db";
16import { users, repositories, gateRuns } from "../db/schema";
17import { Layout } from "../views/layout";
18import { softAuth } from "../middleware/auth";
19import type { AuthEnv } from "../middleware/auth";
20import { getLastTick, getTickCount } from "../lib/autopilot";
b1be050CC LABS App21import { recentRedChecks } from "../lib/synthetic-monitor";
2316be6Claude22
23const status = new Hono<AuthEnv>();
24status.use("*", softAuth);
25
26const started = Date.now();
27
28function fmtUptime(ms: number): string {
29 const s = Math.floor(ms / 1000);
30 const d = Math.floor(s / 86400);
31 const h = Math.floor((s % 86400) / 3600);
32 const m = Math.floor((s % 3600) / 60);
33 if (d > 0) return `${d}d ${h}h`;
34 if (h > 0) return `${h}h ${m}m`;
35 return `${m}m`;
36}
37
38status.get("/status", async (c) => {
39 const user = c.get("user");
40
41 let dbOk = false;
42 try {
43 await db.execute(sql`SELECT 1`);
44 dbOk = true;
45 } catch {
46 dbOk = false;
47 }
48
49 let userCount = 0;
50 let repoCount = 0;
51 let publicRepoCount = 0;
52 let gateRunCount = 0;
53 let greenRate: number | null = null;
54 try {
55 const [u] = await db.select({ n: sql<number>`count(*)::int` }).from(users);
56 userCount = Number(u?.n ?? 0);
57 const [r] = await db
58 .select({ n: sql<number>`count(*)::int` })
59 .from(repositories);
60 repoCount = Number(r?.n ?? 0);
61 const [pr] = await db
62 .select({ n: sql<number>`count(*)::int` })
63 .from(repositories)
64 .where(sql`${repositories.isPrivate} = false`);
65 publicRepoCount = Number(pr?.n ?? 0);
66 const [gr] = await db
67 .select({ n: sql<number>`count(*)::int` })
68 .from(gateRuns);
69 gateRunCount = Number(gr?.n ?? 0);
70 if (gateRunCount > 0) {
71 const [g] = await db
72 .select({ n: sql<number>`count(*)::int` })
73 .from(gateRuns)
74 .where(sql`${gateRuns.status} IN ('passed','repaired')`);
75 greenRate = (Number(g?.n ?? 0) / gateRunCount) * 100;
76 }
77 } catch {
78 // counts stay 0
79 }
80
81 const tick = getLastTick();
82 const ticks = getTickCount();
83 const autopilotDisabled = process.env.AUTOPILOT_DISABLED === "1";
84 const uptimeMs = Date.now() - started;
85
b1be050CC LABS App86 // BLOCK S4 — Show any red synthetic-monitor results from the last 24h
87 // on the public status page. Never blocks the render.
88 let recentIncidents: Awaited<ReturnType<typeof recentRedChecks>> = [];
89 try {
90 recentIncidents = await recentRedChecks(24, 10);
91 } catch {
92 recentIncidents = [];
93 }
94
95 const overallOk = dbOk && recentIncidents.length === 0;
2316be6Claude96
97 return c.html(
98 <Layout title="Status — gluecron" user={user}>
99 <div style="max-width: 960px; margin: 0 auto; padding: 24px 16px">
100 <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 8px">
101 <span
102 style={`display: inline-block; width: 14px; height: 14px; border-radius: 50%; background: ${overallOk ? "var(--green, #2da44e)" : "var(--red, #cf222e)"}`}
103 />
104 <h1 style="margin: 0; font-size: 28px">
105 {overallOk ? "All systems operational" : "Service degraded"}
106 </h1>
107 </div>
108 <p style="color: var(--text-muted); margin-bottom: 32px">
109 Live platform status. Reloads on refresh; no client-side polling.
110 </p>
111
112 <h2 style="margin-bottom: 12px; font-size: 18px">Components</h2>
113 <div class="panel" style="margin-bottom: 24px">
114 <div
115 class="panel-item"
116 style="justify-content: space-between; align-items: center"
117 >
118 <div>
119 <strong>Database</strong>
120 <div style="font-size: 12px; color: var(--text-muted)">
121 Neon PostgreSQL
122 </div>
123 </div>
124 <span
125 style={`padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; background: ${dbOk ? "rgba(45, 164, 78, 0.15)" : "rgba(207, 34, 46, 0.15)"}; color: ${dbOk ? "var(--green, #2da44e)" : "var(--red, #cf222e)"}`}
126 >
127 {dbOk ? "operational" : "down"}
128 </span>
129 </div>
130 <div
131 class="panel-item"
132 style="justify-content: space-between; align-items: center"
133 >
134 <div>
135 <strong>Autopilot</strong>
136 <div style="font-size: 12px; color: var(--text-muted)">
137 Periodic platform-maintenance loop
138 </div>
139 </div>
140 <span
141 style={`padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; background: ${autopilotDisabled ? "rgba(150, 150, 150, 0.15)" : "rgba(45, 164, 78, 0.15)"}; color: ${autopilotDisabled ? "var(--text-muted)" : "var(--green, #2da44e)"}`}
142 >
143 {autopilotDisabled ? "disabled" : "running"}
144 </span>
145 </div>
146 <div
147 class="panel-item"
148 style="justify-content: space-between; align-items: center"
149 >
150 <div>
151 <strong>Git Smart HTTP</strong>
152 <div style="font-size: 12px; color: var(--text-muted)">
153 Clone, fetch, push
154 </div>
155 </div>
156 <span style="padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; background: rgba(45, 164, 78, 0.15); color: var(--green, #2da44e)">
157 operational
158 </span>
159 </div>
160 </div>
161
162 <h2 style="margin-bottom: 12px; font-size: 18px">Platform stats</h2>
163 <div
164 style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 24px"
165 >
166 <div class="panel" style="padding: 14px; text-align: center">
167 <div style="font-size: 24px; font-weight: 700">
168 {userCount.toLocaleString()}
169 </div>
170 <div
171 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
172 >
173 Developers
174 </div>
175 </div>
176 <div class="panel" style="padding: 14px; text-align: center">
177 <div style="font-size: 24px; font-weight: 700">
178 {repoCount.toLocaleString()}
179 </div>
180 <div
181 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
182 >
183 Repositories
184 </div>
185 </div>
186 <div class="panel" style="padding: 14px; text-align: center">
187 <div style="font-size: 24px; font-weight: 700">
188 {publicRepoCount.toLocaleString()}
189 </div>
190 <div
191 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
192 >
193 Public repos
194 </div>
195 </div>
196 <div class="panel" style="padding: 14px; text-align: center">
197 <div style="font-size: 24px; font-weight: 700">
198 {gateRunCount.toLocaleString()}
199 </div>
200 <div
201 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
202 >
203 Gate runs
204 </div>
205 </div>
206 <div class="panel" style="padding: 14px; text-align: center">
207 <div style="font-size: 24px; font-weight: 700">
208 {greenRate === null ? "—" : `${greenRate.toFixed(1)}%`}
209 </div>
210 <div
211 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
212 >
213 Green rate
214 </div>
215 </div>
216 <div class="panel" style="padding: 14px; text-align: center">
217 <div style="font-size: 24px; font-weight: 700">
218 {fmtUptime(uptimeMs)}
219 </div>
220 <div
221 style="font-size: 11px; color: var(--text-muted); text-transform: uppercase"
222 >
223 Uptime
224 </div>
225 </div>
226 </div>
227
b1be050CC LABS App228 {recentIncidents.length > 0 ? (
229 <>
230 <h2 style="margin-bottom: 12px; font-size: 18px">
231 Recent incidents (last 24h)
232 </h2>
233 <div class="panel" style="margin-bottom: 24px">
234 {recentIncidents.map((r) => (
235 <div
236 class="panel-item"
237 style="justify-content: space-between; font-size: 13px"
238 >
239 <div>
240 <code>{r.name}</code>
241 <span style="color: var(--text-muted); margin-left: 8px">
242 — {r.error || "(no error message)"}
243 </span>
244 </div>
245 <span style="color: var(--text-muted); font-size: 12px">
246 {r.checkedAt.toISOString()}
247 </span>
248 </div>
249 ))}
250 </div>
251 </>
252 ) : null}
253
2316be6Claude254 <h2 style="margin-bottom: 12px; font-size: 18px">
255 Latest autopilot tick
256 </h2>
257 {tick ? (
258 <div class="panel" style="margin-bottom: 24px">
259 <div
260 class="panel-item"
261 style="justify-content: space-between; font-size: 13px"
262 >
263 <span>Finished</span>
264 <code>{tick.finishedAt}</code>
265 </div>
266 <div
267 class="panel-item"
268 style="justify-content: space-between; font-size: 13px"
269 >
270 <span>Total ticks this process</span>
271 <code>{ticks}</code>
272 </div>
273 {tick.tasks.map((t) => (
274 <div
275 class="panel-item"
276 style="justify-content: space-between; font-size: 13px"
277 >
278 <code>{t.name}</code>
279 <span
280 style={
281 t.ok
282 ? "color: var(--green, #2da44e)"
283 : "color: var(--red, #cf222e)"
284 }
285 >
286 {t.ok ? "ok" : `failed: ${t.error || "unknown"}`}
287 <span
288 style="color: var(--text-muted); margin-left: 8px"
289 >
290 {t.durationMs}ms
291 </span>
292 </span>
293 </div>
294 ))}
295 </div>
296 ) : (
297 <p
298 style="color: var(--text-muted); margin-bottom: 24px; font-size: 14px"
299 >
300 {autopilotDisabled
301 ? "Autopilot is disabled via AUTOPILOT_DISABLED=1."
302 : "No ticks have completed yet. Check back after the first 5-minute interval elapses."}
303 </p>
304 )}
305
306 <p
307 style="color: var(--text-muted); font-size: 12px; margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--border)"
308 >
309 Liveness: <a href="/healthz">/healthz</a> &middot; Readiness:{" "}
310 <a href="/readyz">/readyz</a> &middot; Metrics:{" "}
311 <a href="/metrics">/metrics</a> &middot; Platform JSON:{" "}
312 <a href="/api/platform-status">/api/platform-status</a>
313 </p>
314 </div>
315 </Layout>
316 );
317});
318
9b07ca9Claude319/**
320 * Shields-style status badge. Reads the latest autopilot tick + DB
321 * reachability and returns an SVG. Embed in READMEs with:
322 * ![status](https://your-host/status.svg)
323 */
324status.get("/status.svg", async (c) => {
325 let dbOk = false;
326 try {
327 await db.execute(sql`SELECT 1`);
328 dbOk = true;
329 } catch {
330 dbOk = false;
331 }
332 const tick = getLastTick();
333 const lastOk = tick ? tick.tasks.every((t) => t.ok) : true;
334 const overall = dbOk && lastOk;
335 const label = "gluecron";
336 const value = overall ? "operational" : "degraded";
337 const fill = overall ? "#2da44e" : "#cf222e";
338
339 const labelW = 70;
340 const valueW = overall ? 78 : 68;
341 const totalW = labelW + valueW;
342 const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="20" role="img" aria-label="${label}: ${value}">
343 <linearGradient id="s" x2="0" y2="100%">
344 <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
345 <stop offset="1" stop-opacity=".1"/>
346 </linearGradient>
347 <rect width="${totalW}" height="20" rx="3" fill="#555"/>
348 <rect x="${labelW}" width="${valueW}" height="20" rx="3" fill="${fill}"/>
349 <rect width="${totalW}" height="20" rx="3" fill="url(#s)"/>
350 <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,sans-serif" font-size="11">
351 <text x="${labelW / 2}" y="15">${label}</text>
352 <text x="${labelW + valueW / 2}" y="15">${value}</text>
353 </g>
354</svg>`;
355 c.header("Content-Type", "image/svg+xml; charset=utf-8");
356 c.header("Cache-Control", "no-cache, max-age=0");
357 return c.body(svg);
358});
359
2316be6Claude360export default status;