diff --git a/sites/sh1pt.com/app/api/health/route.ts b/sites/sh1pt.com/app/api/health/route.ts new file mode 100644 index 00000000..46653376 --- /dev/null +++ b/sites/sh1pt.com/app/api/health/route.ts @@ -0,0 +1,27 @@ +// Public health check for status.profullstack.com: one HEAD select against +// profiles, capped at 3s. Never returns error details. + +import { NextResponse } from 'next/server'; +import { getSupabaseServiceClient } from '@/lib/supabase/service'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; + +const HEADERS = { 'Cache-Control': 'no-store' }; + +export async function GET() { + try { + const { error } = await getSupabaseServiceClient() + .from('profiles') + .select('id', { head: true }) + .limit(1) + .abortSignal(AbortSignal.timeout(3000)); + if (error) throw error; + return NextResponse.json({ status: 'ok', db: 'ok' }, { headers: HEADERS }); + } catch { + return NextResponse.json( + { status: 'error', db: 'down' }, + { status: 503, headers: HEADERS }, + ); + } +} diff --git a/sites/sh1pt.com/middleware.ts b/sites/sh1pt.com/middleware.ts index 7d95a3ae..5823a9c7 100644 --- a/sites/sh1pt.com/middleware.ts +++ b/sites/sh1pt.com/middleware.ts @@ -2,6 +2,9 @@ import { gate } from "@/lib/crawl-gateway"; import { NextResponse, type NextRequest } from 'next/server'; export async function middleware(request: NextRequest) { + // The status page's health check is never gated or redirected. + if (request.nextUrl.pathname === '/api/health') return NextResponse.next(); + // Crawl gateway first: AI training crawlers get 402 Payment Required (or the // sales page at /crawl) unless they present a paid pass. People, Googlebot // and retrieval crawlers fall through to everything below.