From b41a6d0af794ee77bd67967249a751c9e68b9887 Mon Sep 17 00:00:00 2001 From: Stefan Lazic Date: Fri, 7 Aug 2026 18:06:39 +0100 Subject: [PATCH] Create route.ts --- src/app/api/debug/db-connect-test/route.ts | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/app/api/debug/db-connect-test/route.ts diff --git a/src/app/api/debug/db-connect-test/route.ts b/src/app/api/debug/db-connect-test/route.ts new file mode 100644 index 0000000..1329fa5 --- /dev/null +++ b/src/app/api/debug/db-connect-test/route.ts @@ -0,0 +1,79 @@ +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 }, + ); + } +}