← back to Trademarks Copyright

src/app/api/health/route.ts

44 lines

import { NextResponse } from "next/server";
import { query } from "@/lib/db";
import { qwenIsUp, QWEN_MODEL_NAME } from "@/lib/qwen";
import { currentBackend } from "@/lib/email";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

export async function GET() {
  const start = Date.now();
  const checks: Record<string, { ok: boolean; detail?: string; latencyMs?: number }> = {};

  try {
    const t0 = Date.now();
    const { rows } = await query<{ n: string }>("SELECT COUNT(*)::text AS n FROM items");
    checks.db = { ok: true, detail: `${rows[0].n} items`, latencyMs: Date.now() - t0 };
  } catch (e) {
    checks.db = { ok: false, detail: (e as Error).message };
  }

  try {
    const t0 = Date.now();
    const up = await qwenIsUp();
    checks.ollama = { ok: up, detail: up ? QWEN_MODEL_NAME : "not reachable", latencyMs: Date.now() - t0 };
  } catch (e) {
    checks.ollama = { ok: false, detail: (e as Error).message };
  }

  checks.email_backend = { ok: true, detail: currentBackend() };
  checks.stripe = { ok: true, detail: process.env.STRIPE_SECRET_KEY ? "configured" : "dev-mock" };

  const overall = Object.values(checks).every((c) => c.ok);
  return NextResponse.json(
    {
      ok: overall,
      timestamp: new Date().toISOString(),
      uptime_s: Math.floor(process.uptime()),
      latency_ms: Date.now() - start,
      checks,
    },
    { status: overall ? 200 : 503 }
  );
}