import { performance } from "perf_hooks"; import { db } from "@homarr/db"; import { logger } from "@homarr/log"; import { handshakeAsync } from "@homarr/redis"; export async function GET() { const timeBeforeHealthCheck = performance.now(); const response = await executeAndAggregateAllHealthChecksAsync(); logger.info(`Completed healthcheck after ${performance.now() - timeBeforeHealthCheck}ms`); if (response.status === "healthy") { return new Response(JSON.stringify(response), { status: 200, }); } return new Response(JSON.stringify(response), { status: 500, }); } const executeAndAggregateAllHealthChecksAsync = async (): Promise<{ healthChecks: Record; status: "healthy" | "unhealthy"; }> => { const healthChecks = [ executeHealthCheckSafelyAsync("database", async () => { // sqlite driver does not support raw query execution. this is for a heartbeat check only - it doesn't matter if data is returned or not await db.query.serverSettings.findFirst(); return {}; }), executeHealthCheckSafelyAsync("redis", async () => { await handshakeAsync(); return {}; }), ]; const healthCheckResults = await Promise.all(healthChecks); const anyUnhealthy = healthCheckResults.some((healthCheck) => healthCheck.status === "unhealthy"); const healthCheckValues = healthCheckResults.reduce( (acc, healthCheck) => { acc[healthCheck.name] = { status: healthCheck.status, ...healthCheck.values, }; return acc; }, {} as Record, ); return { status: anyUnhealthy ? "unhealthy" : "healthy", healthChecks: healthCheckValues, }; }; const executeHealthCheckSafelyAsync = async ( name: string, callback: () => Promise, ): Promise => { try { const currentTimeBeforeCallback = performance.now(); const values = await callback(); return { name, status: "healthy", values: { ...values, latency: performance.now() - currentTimeBeforeCallback, }, }; } catch (error) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions logger.error(`Healthcheck '${name}' has failed: ${error}`); return { status: "unhealthy", values: { error, }, name, }; } }; interface HealthCheckResult { status: "healthy" | "unhealthy"; name: string; values: object; }