feat: add production health smoke

This commit is contained in:
732642856
2026-07-18 01:22:35 +08:00
parent 42e4e29ac1
commit 8f0cafbb67
3 changed files with 173 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import { NextResponse } from "next/server";
type Check = {
status: "ok" | "degraded" | "blocked";
message?: string;
latencyMs?: number;
};
const jyotishApiBase = process.env.JYOTISH_API_BASE ?? "http://127.0.0.1:5200";
function envCheck(names: string[]): Check {
const missing = names.filter((name) => !process.env[name]);
return missing.length
? { status: "blocked", message: `missing:${missing.join(",")}` }
: { status: "ok" };
}
function anyEnvCheck(names: string[]): Check {
return names.some((name) => process.env[name])
? { status: "ok" }
: { status: "blocked", message: `missing_one_of:${names.join("|")}` };
}
async function jyotishApiCheck(): Promise<Check> {
const started = Date.now();
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
try {
const response = await fetch(`${jyotishApiBase}/api/health`, {
cache: "no-store",
signal: controller.signal,
});
return {
status: response.ok ? "ok" : "degraded",
message: response.ok ? undefined : `http:${response.status}`,
latencyMs: Date.now() - started,
};
} catch (error) {
return {
status: "blocked",
message: error instanceof Error ? error.name : "jyotish_api_unavailable",
latencyMs: Date.now() - started,
};
} finally {
clearTimeout(timeout);
}
}
function aggregate(checks: Record<string, Check>) {
if (Object.values(checks).some((check) => check.status === "blocked")) return "blocked";
if (Object.values(checks).some((check) => check.status === "degraded")) return "degraded";
return "ok";
}
export async function GET() {
const checks = {
web: { status: "ok" } satisfies Check,
supabasePublicConfig: envCheck(["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"]),
supabaseServiceRole: envCheck(["SUPABASE_SERVICE_ROLE_KEY"]),
modelProvider: anyEnvCheck(["LLM_MODELS_JSON", "OPENAI_API_KEY", "LLM_API_KEY", "DEEPSEEK_API_KEY"]),
jyotishApi: await jyotishApiCheck(),
};
const status = aggregate(checks);
return NextResponse.json(
{
status,
timestamp: new Date().toISOString(),
checks,
},
{ status: status === "ok" ? 200 : 503 },
);
}