Files
SRCmail/app/api/health/route.ts
T
Matthieu MALVACHEandMatthieu MALVACHE fb414bf2c9 feat: add contacts phase 2, advanced search, vacation responder, Docker & TOTP 2FA
- Contact groups/lists, vCard import/export (RFC 6350), bulk operations
- Advanced search with JMAP filter panel, search chips, cross-mailbox queries
- Vacation responder with JMAP VacationResponse, settings tab, sidebar indicator
- TOTP two-factor authentication support
- Docker multi-stage build with standalone output and docker-compose
- CSP Report-Only headers and security headers via proxy middleware
- Virtual scrolling for large email lists
- Structured server-side logger (text/JSON, configurable level)
- 450+ tests (contacts, vCard, threads, headers, identity, components)
- Playwright E2E framework setup
- Updated README and ROADMAP with all new features
2026-02-16 18:51:30 +01:00

131 lines
3.8 KiB
TypeScript

import { NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { logger } from '@/lib/logger';
// Health check thresholds
const MEMORY_WARNING_THRESHOLD = 0.85; // 85% heap usage
const MEMORY_CRITICAL_THRESHOLD = 0.95; // 95% heap usage
interface HealthStatus {
status: 'healthy' | 'degraded' | 'unhealthy';
timestamp: string;
uptime?: number;
version?: string;
memory?: {
heapUsed: number;
heapTotal: number;
rss: number;
external: number;
heapUsagePercent: number;
};
environment?: string;
nodeVersion?: string;
warnings?: string[];
reason?: string;
}
/**
* Health check endpoint for container orchestration
*
* GET /api/health - Basic health check (returns 200 OK or 503 Service Unavailable)
* GET /api/health?detailed=true - Detailed diagnostics with memory stats
* HEAD /api/health - Lightweight health check (status code only)
*
* Health status based on Node.js heap usage:
* - Healthy (200): < 85% heap usage
* - Degraded (200): 85-95% heap usage (warnings in detailed mode)
* - Unhealthy (503): > 95% heap usage
*/
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const detailed = searchParams.get('detailed') === 'true';
try {
const timestamp = new Date().toISOString();
const memUsage = process.memoryUsage();
const heapUsagePercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
// Determine health status based on memory usage
let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';
const warnings: string[] = [];
let httpStatus = 200;
if (heapUsagePercent >= MEMORY_CRITICAL_THRESHOLD * 100) {
status = 'unhealthy';
httpStatus = 503;
} else if (heapUsagePercent >= MEMORY_WARNING_THRESHOLD * 100) {
status = 'degraded';
warnings.push(`Memory usage high: ${heapUsagePercent.toFixed(1)}%`);
}
// Build response
const response: HealthStatus = {
status,
timestamp,
};
if (status === 'unhealthy') {
response.reason = `Memory usage critical: ${heapUsagePercent.toFixed(1)}%`;
}
// Add detailed information if requested
if (detailed) {
response.uptime = process.uptime();
response.version = process.env.npm_package_version || '0.1.0';
response.memory = {
heapUsed: memUsage.heapUsed,
heapTotal: memUsage.heapTotal,
rss: memUsage.rss,
external: memUsage.external,
heapUsagePercent: Number(heapUsagePercent.toFixed(2)),
};
response.environment = process.env.NODE_ENV || 'development';
response.nodeVersion = process.version;
if (warnings.length > 0) {
response.warnings = warnings;
}
}
logger.info('Health check', { status, detailed });
return NextResponse.json(response, {
status: httpStatus,
headers: {
'Cache-Control': 'no-store, no-cache, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
},
});
} catch (error) {
logger.error('Health check failed', { error: error instanceof Error ? error.message : 'Unknown error' });
return NextResponse.json(
{
status: 'unhealthy',
timestamp: new Date().toISOString(),
reason: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 503 }
);
}
}
/**
* HEAD method for ultra-lightweight health checks
*/
export async function HEAD() {
try {
const memUsage = process.memoryUsage();
const heapUsagePercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
if (heapUsagePercent >= MEMORY_CRITICAL_THRESHOLD * 100) {
return new Response(null, { status: 503 });
}
return new Response(null, { status: 200 });
} catch {
return new Response(null, { status: 503 });
}
}