From 01e3457512966c884c093483deaebabcc14fcc2c Mon Sep 17 00:00:00 2001 From: denverm Date: Thu, 15 Jan 2026 17:08:14 +0200 Subject: [PATCH] health check passes even if database is down --- src/app/api/health/route.ts | 64 ++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts index c3183b5..26f1654 100644 --- a/src/app/api/health/route.ts +++ b/src/app/api/health/route.ts @@ -46,22 +46,30 @@ export async function GET(request: Request) { }; // Check database connectivity - try { - const dbStart = Date.now(); - await prisma.$queryRaw`SELECT 1`; - const dbLatency = Date.now() - dbStart; + if (process.env.SKIP_HEALTH_DB_CHECK === '1') { services.database = { - status: 'healthy', - latency: dbLatency, - message: 'Connected', - timestamp: new Date().toISOString(), - }; - } catch (error) { - services.database = { - status: 'down', - message: error instanceof Error ? error.message : 'Connection failed', + status: 'unknown', + message: 'Database check skipped (SKIP_HEALTH_DB_CHECK=1)', timestamp: new Date().toISOString(), }; + } else { + try { + const dbStart = Date.now(); + await prisma.$queryRaw`SELECT 1`; + const dbLatency = Date.now() - dbStart; + services.database = { + status: 'healthy', + latency: dbLatency, + message: 'Connected', + timestamp: new Date().toISOString(), + }; + } catch (error) { + services.database = { + status: 'down', + message: error instanceof Error ? error.message : 'Connection failed', + timestamp: new Date().toISOString(), + }; + } } // Check Redis connectivity @@ -277,18 +285,19 @@ export async function GET(request: Request) { } // Determine overall status - // Critical services: Database, Environment (critical vars only) + // Critical services: Database (unless skipped), Environment (critical vars only) // Optional services: Redis, Apple Music, CSRF_SECRET - + + const skipDbCheck = process.env.SKIP_HEALTH_DB_CHECK === '1'; const dbDown = services.database.status === 'down'; - const dbHealthy = services.database.status === 'healthy'; + const dbHealthy = services.database.status === 'healthy' || services.database.status === 'unknown'; // Unknown is OK if skipped const envDown = services.environment.status === 'down'; const envHealthy = services.environment.status === 'healthy'; const envDegraded = services.environment.status === 'degraded'; // Only optional vars missing - - // Critical services check - const criticalServicesDown = dbDown || envDown; - const criticalServicesHealthy = dbHealthy && (envHealthy || envDegraded); // Degraded env is OK (only optional vars missing) + + // Critical services check - database is critical unless explicitly skipped + const criticalServicesDown = (dbDown && !skipDbCheck) || envDown; + const criticalServicesHealthy = (dbHealthy || skipDbCheck) && (envHealthy || envDegraded); // Degraded env is OK (only optional vars missing) // Optional services status (for display only, don't affect overall status) const optionalServicesUnknown = @@ -360,10 +369,21 @@ export async function GET(request: Request) { // Also support HEAD requests for lightweight health checks export async function HEAD() { + // Skip database check if requested + if (process.env.SKIP_HEALTH_DB_CHECK === '1') { + return new NextResponse(null, { + status: 200, + headers: { + // Cache HEAD responses for 10 seconds + 'Cache-Control': 'public, max-age=10, stale-while-revalidate=30', + }, + }); + } + try { // Quick database check only - lightweight for load balancers await prisma.$queryRaw`SELECT 1`; - return new NextResponse(null, { + return new NextResponse(null, { status: 200, headers: { // Cache HEAD responses for 10 seconds @@ -371,7 +391,7 @@ export async function HEAD() { }, }); } catch { - return new NextResponse(null, { + return new NextResponse(null, { status: 503, headers: { 'Cache-Control': 'no-cache', // Don't cache failures