feat: expose health probes and metrics

This commit is contained in:
2026-07-19 12:49:39 +08:00
parent 6a10ddb6e5
commit 428ee50cd6
6 changed files with 27 additions and 2 deletions
+4 -2
View File
@@ -2,10 +2,12 @@ import { NextResponse } from "next/server";
import { db } from "@/lib/db";
export const dynamic = "force-dynamic";
export async function GET() {
export async function GET(request: Request) {
const probe = new URL(request.url).searchParams.get("probe");
if (probe === "live") return NextResponse.json({ ok: true, status: "live", version: process.env.APP_VERSION || "development", timestamp: new Date().toISOString() });
try {
db.prepare("SELECT 1").get();
const failedJobs = Number((db.prepare("SELECT count(*) AS count FROM sync_jobs WHERE status='failed'").get() as { count: number }).count);
return NextResponse.json({ ok: true, version: process.env.APP_VERSION || "development", database: "ok", failedJobs, timestamp: new Date().toISOString() });
return NextResponse.json({ ok: true, status: "ready", version: process.env.APP_VERSION || "development", database: "ok", failedJobs, timestamp: new Date().toISOString() });
} catch { return NextResponse.json({ ok: false, database: "error" }, { status: 503 }); }
}
+5
View File
@@ -0,0 +1,5 @@
import { timingSafeEqual } from "node:crypto";
import { NextResponse } from "next/server";
import { prometheusMetrics } from "@/lib/metrics";
export const dynamic = "force-dynamic";
export async function GET(request: Request) { const expected = process.env.METRICS_TOKEN; const supplied = request.headers.get("authorization")?.replace(/^Bearer\s+/i, "") || ""; if (expected) { const actual = Buffer.from(supplied), target = Buffer.from(expected); if (actual.length !== target.length || !timingSafeEqual(actual, target)) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } return new NextResponse(prometheusMetrics(), { headers: { "Content-Type": "text/plain; version=0.0.4; charset=utf-8", "Cache-Control": "no-store" } }); }