import mariadb from "mariadb"; import { NextResponse } from "next/server"; import { secretsMatch } from "@/lib/security/secrets-match"; /** * TEMPORARY — diagnose the "pool timeout" seen on the Vercel+Hostinger * deployment. Prisma's connection pool wraps every failure as the same * generic "pool timeout" message, hiding the actual socket-level error * (ETIMEDOUT = firewall silently dropping packets, ECONNREFUSED = port * closed, ER_ACCESS_DENIED_ERROR = wrong credentials, etc.) that would * otherwise tell us exactly what's blocking the connection. This makes * one raw `mariadb.createConnection()` attempt, bypassing the pool * entirely, and reports that real error back. Delete this route once the * connectivity issue is resolved — an authenticated diagnostic endpoint * has no reason to stay in a shipped app. * * Auth: reuses CRON_SECRET (already configured for * /api/cron/process-notifications) rather than adding a new secret only * this throwaway route would ever use. */ export async function GET(request: Request) { const secret = process.env.CRON_SECRET; if (!secret) { return NextResponse.json({ error: "CRON_SECRET is not configured on this deployment." }, { status: 503 }); } const provided = request.headers.get("authorization"); if (!provided || !secretsMatch(provided, `Bearer ${secret}`)) { return NextResponse.json({ error: "Unauthorized." }, { status: 401 }); } const host = process.env.DATABASE_HOST; const port = process.env.DATABASE_PORT ? Number(process.env.DATABASE_PORT) : 3306; const database = process.env.DATABASE_NAME; const user = process.env.DATABASE_USER; const password = process.env.DATABASE_PASSWORD; const ssl = process.env.DATABASE_SSL === "true"; if (!host || !database || !user || password === undefined) { return NextResponse.json({ error: "DATABASE_* env vars are not fully configured." }, { status: 503 }); } const startedAt = Date.now(); try { const conn = await mariadb.createConnection({ host, port, user, password, database, connectTimeout: 8000, ssl: ssl ? { rejectUnauthorized: true } : undefined, }); await conn.query("SELECT 1"); await conn.end(); return NextResponse.json({ success: true, durationMs: Date.now() - startedAt, host, port, ssl, }); } catch (error) { const err = error as { code?: string; errno?: number; message?: string }; return NextResponse.json( { success: false, durationMs: Date.now() - startedAt, host, port, ssl, errorCode: err.code ?? null, errorNo: err.errno ?? null, errorMessage: err.message ?? String(error), }, { status: 502 }, ); } }