Skip to content

Commit 62cffd5

Browse files
ralyodioclaude
andauthored
ops: add public GET /api/health for status.profullstack.com (#345)
Returns {"status":"ok","db":"ok"} after a head-only PostgREST query with a 3s timeout, or 503 {"status":"error","db":"down"}. Cache-Control: no-store. The proxy lets the path straight through, skipping the crawl gateway and the session refresh. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 2e07e71 commit 62cffd5

2 files changed

Lines changed: 28 additions & 0 deletions

File tree

‎app/api/health/route.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextResponse } from "next/server";
2+
import { serviceClient } from "@/lib/supabase/service";
3+
4+
export const runtime = "nodejs";
5+
// A cached answer reports the cache's health, not the app's.
6+
export const dynamic = "force-dynamic";
7+
8+
const headers = { "cache-control": "no-store" };
9+
10+
/**
11+
* Public liveness + database check for status.profullstack.com. One head-only
12+
* PostgREST count with a 3s cap; the reason for a failure is never returned.
13+
*/
14+
export async function GET() {
15+
try {
16+
const { error } = await serviceClient()
17+
.from("projects")
18+
.select("id", { head: true })
19+
.limit(1)
20+
.abortSignal(AbortSignal.timeout(3000));
21+
if (error) throw error;
22+
} catch {
23+
return NextResponse.json({ status: "error", db: "down" }, { status: 503, headers });
24+
}
25+
return NextResponse.json({ status: "ok", db: "ok" }, { headers });
26+
}

‎proxy.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { isNavigation } from "@/lib/affiliate/cookie";
88
type Cookie = { name: string; value: string; options?: CookieOptions };
99

1010
export async function proxy(request: NextRequest) {
11+
// The status page's health probe: no gateway, no session refresh.
12+
if (request.nextUrl.pathname === "/api/health") return NextResponse.next();
1113
// Ad routes enforce their gate themselves, before DB work. Avoid counting a
1214
// request twice or making an unrelated Supabase Auth call for each click.
1315
if (isAdClickPath(request.nextUrl.pathname)) return NextResponse.next();

0 commit comments

Comments
 (0)