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

version.ts

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

version.tsBlame97 lines · 2 contributors
05cdb85Claude1/**
2 * /api/version — public build-info endpoint.
3 *
4 * Returns the running process's commit SHA, branch, boot time, and uptime
5 * as a tiny JSON payload. Used by:
6 * - The client-side auto-update banner (polls every 15s, prompts reload
7 * when sha changes)
8 * - Operators sanity-checking 'did my push actually deploy?'
9 * - Monitoring (latency to seeing a new sha = end-to-end deploy time)
10 *
11 * Cache-control: no-store. Must be live, never cached.
b1be050CC LABS App12 *
13 * Block S3 (2026-05-14): additively reports the 5 most recently applied
14 * migrations from the live DB so the post-deploy smoke suite can verify
15 * the latest drizzle/*.sql file actually landed in the running schema.
16 * The migrations field is best-effort: if the DB query fails or the
17 * connection isn't configured the field is omitted, never throws.
05cdb85Claude18 */
19
20import { Hono } from "hono";
b1be050CC LABS App21import { sql } from "drizzle-orm";
05cdb85Claude22import { getBuildInfo } from "../lib/build-info";
b1be050CC LABS App23import { db } from "../db";
05cdb85Claude24
25const version = new Hono();
26
b1be050CC LABS App27// In-process cache for the migrations list. /api/version is polled
28// every 15s by the auto-update banner — re-querying _migrations on
29// every hit would be wasteful. 10s TTL is a reasonable compromise: a
30// fresh deploy's new migration will show up in the smoke check within
31// at most 10s of when migrate.ts inserted the row.
32const MIGRATIONS_CACHE_TTL_MS = 10_000;
33let _migrationsCache: { at: number; names: string[] } | null = null;
34
35async function readRecentMigrations(): Promise<string[] | null> {
36 const now = Date.now();
37 if (_migrationsCache && now - _migrationsCache.at < MIGRATIONS_CACHE_TTL_MS) {
38 return _migrationsCache.names;
39 }
40 try {
41 // _migrations is created on first migration run; if the table doesn't
42 // exist (very first boot before migrate.ts has ever run) we degrade
43 // silently to an empty list.
44 const rows = (await db.execute(
45 sql`SELECT name FROM _migrations ORDER BY applied_at DESC LIMIT 5`
46 )) as unknown as Array<{ name: string }>;
47 const names = (rows ?? []).map((r) => r.name).filter(Boolean);
48 _migrationsCache = { at: now, names };
49 return names;
50 } catch {
51 // _migrations table missing, DB down, etc. — return null so the
52 // endpoint can omit the field without 500ing.
53 return null;
54 }
55}
56
57/**
58 * Test seam: lets `src/__tests__/post-deploy-smoke.test.ts` reset the
59 * cache between assertions. Not part of the public API.
60 */
61export const __test = {
62 clearMigrationsCache: () => {
63 _migrationsCache = null;
64 },
65};
66
67version.get("/api/version", async (c) => {
68 c.header("cache-control", "no-store, no-cache, must-revalidate");
69 c.header("pragma", "no-cache");
70 const build = getBuildInfo();
71 const migrations = await readRecentMigrations();
72 if (migrations !== null) {
73 return c.json({ ...build, migrations });
74 }
75 return c.json(build);
76});
77
78// Block S2 — minimal `/version` alias used by the service-worker cache
79// bust + external uptime/deploy monitors. Kept additive (single new
80// handler, no edits to existing shape) so the S1+S3 smoke-suite agent's
81// edits to this file can land alongside without merge conflicts.
82//
83// S3 (2026-05-14): mirror the migrations field here so the smoke
84// suite can check either endpoint.
85version.get("/version", async (c) => {
05cdb85Claude86 c.header("cache-control", "no-store, no-cache, must-revalidate");
87 c.header("pragma", "no-cache");
b1be050CC LABS App88 const migrations = await readRecentMigrations();
89 const body: Record<string, unknown> = {
90 sha: process.env.BUILD_SHA || "dev",
91 buildAt: process.env.BUILD_TIME || null,
92 };
93 if (migrations !== null) body.migrations = migrations;
94 return c.json(body);
05cdb85Claude95});
96
97export default version;