106 lines
2.9 KiB
TypeScript
106 lines
2.9 KiB
TypeScript
export interface ServiceTarget {
|
|
name: string;
|
|
url: string;
|
|
path: string;
|
|
}
|
|
|
|
export interface ServiceCheck {
|
|
name: string;
|
|
url: string;
|
|
status: 'healthy' | 'unhealthy' | 'unreachable';
|
|
responseTimeMs: number;
|
|
details?: Record<string, unknown>;
|
|
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<ServiceCheck> {
|
|
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<string, unknown> | undefined;
|
|
try {
|
|
details = (await res.json()) as Record<string, unknown>;
|
|
} 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<HealthReport> {
|
|
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 },
|
|
};
|
|
}
|