export interface ServiceTarget { name: string; url: string; path: string; } export interface ServiceCheck { name: string; url: string; status: 'healthy' | 'unhealthy' | 'unreachable'; responseTimeMs: number; details?: Record; error?: string; } export interface HealthReport { overall: 'healthy' | 'degraded' | 'down'; timestamp: string; services: ServiceCheck[]; summary: { healthy: number; unhealthy: number; unreachable: number; total: number }; } /** * Default service targets (LysnrAI local stack). */ export const DEFAULT_SERVICES: ServiceTarget[] = [ { name: 'Backend API', url: process.env.BACKEND_URL || 'http://localhost:8000', path: '/health' }, { name: 'Platform Service', url: process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003', path: '/health', }, { name: 'Admin Dashboard', url: process.env.ADMIN_DASHBOARD_URL || 'http://localhost:3001', path: '/api/health', }, { name: 'User Dashboard', url: process.env.USER_DASHBOARD_URL || 'http://localhost:3002', path: '/api/health', }, ]; export async function checkService( svc: ServiceTarget, opts?: { timeoutMs?: number } ): Promise { const fullUrl = `${svc.url}${svc.path}`; const start = performance.now(); try { const res = await fetch(fullUrl, { signal: AbortSignal.timeout(opts?.timeoutMs ?? 5_000) }); const elapsed = Math.round(performance.now() - start); if (res.ok) { let details: Record | undefined; try { details = (await res.json()) as Record; } catch { /* ignore */ } return { name: svc.name, url: svc.url, status: 'healthy', responseTimeMs: elapsed, details }; } return { name: svc.name, url: svc.url, status: 'unhealthy', responseTimeMs: elapsed, error: `HTTP ${res.status}`, }; } catch (err) { const elapsed = Math.round(performance.now() - start); return { name: svc.name, url: svc.url, status: 'unreachable', responseTimeMs: elapsed, error: String(err), }; } } export async function generateHealthReport( services: ServiceTarget[] = DEFAULT_SERVICES, opts?: { timeoutMs?: number } ): Promise { const checks = await Promise.all(services.map(svc => checkService(svc, opts))); const healthy = checks.filter(c => c.status === 'healthy').length; const unhealthy = checks.filter(c => c.status === 'unhealthy').length; const unreachable = checks.filter(c => c.status === 'unreachable').length; let overall: HealthReport['overall'] = 'healthy'; if (unreachable === checks.length) overall = 'down'; else if (unhealthy > 0 || unreachable > 0) overall = 'degraded'; return { overall, timestamp: new Date().toISOString(), services: checks, summary: { healthy, unhealthy, unreachable, total: checks.length }, }; }