From 91c5a8f6259a3d21f6bc6da69eadcf75d68c2b07 Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Wed, 12 Aug 2026 23:11:16 -0400 Subject: [PATCH] feat(deploy): add /api/health endpoint for container liveness probes --- src/app/api/health/route.ts | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/app/api/health/route.ts diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 0000000..21179c3 --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,63 @@ +import { existsSync, readFileSync } from "node:fs"; +import path from "node:path"; + +/** + * Lightweight liveness probe for Docker/Coolify healthchecks. + * Returns 200 as soon as the Next.js Route Handler layer is up and able to + * serve a request. We deliberately DO NOT touch the database here: a transient + * DB hiccup should not make Coolify mark the container unhealthy and force a + * restart (which would break in-flight requests against an otherwise healthy + * web process). DB health is tracked separately by the Coolify-managed + * PostgreSQL service. + * + * Path: GET /api/health + */ +export const dynamic = "force-dynamic"; +export const runtime = "nodejs"; + +const startedAt = Date.now(); + +const HEADERS = { + "Content-Type": "application/json", + "Cache-Control": "no-store, no-transform", +} as const; + +type VersionInfo = { + version: string | null; + tag: string | null; + gitDescribe: string | null; + commitHash: string | null; + commitDate: string | null; + commitSubject: string | null; + buildTime: string | null; +}; + +function readVersionInfo(): Partial { + const versionInfoPath = path.join( + process.cwd(), + "src", + "generated", + "versionInfo.json", + ); + try { + if (!existsSync(versionInfoPath)) return {}; + const raw = readFileSync(versionInfoPath, "utf8"); + return JSON.parse(raw) as Partial; + } catch { + return {}; + } +} + +export async function GET(): Promise { + const version = readVersionInfo(); + return new Response( + JSON.stringify({ + ok: true, + uptime: Math.max(0, Math.round((Date.now() - startedAt) / 1000)), + version: version.version ?? null, + commit: version.commitHash ?? null, + buildTime: version.buildTime ?? null, + }), + { status: 200, headers: HEADERS }, + ); +} \ No newline at end of file