mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 09:37:55 -05:00
80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { setTimeout as delay } from 'timers/promises';
|
|
|
|
type ServiceEntry = {
|
|
key: string;
|
|
name: string;
|
|
repoUrl: string;
|
|
railwayUrl?: string | null;
|
|
cloudflareSubdomain?: string | null;
|
|
healthPath?: string;
|
|
};
|
|
|
|
type OsSpec = {
|
|
version: string;
|
|
services: ServiceEntry[];
|
|
};
|
|
|
|
const SPEC_PATH = path.resolve(__dirname, '..', 'os-spec', 'os-spec.json');
|
|
const DEFAULT_TIMEOUT_MS = 8000;
|
|
|
|
function loadSpec(): OsSpec {
|
|
const raw = fs.readFileSync(SPEC_PATH, 'utf-8');
|
|
return JSON.parse(raw) as OsSpec;
|
|
}
|
|
|
|
function buildHealthUrl(service: ServiceEntry): string | null {
|
|
const base = service.cloudflareSubdomain ?? service.railwayUrl;
|
|
if (!base) return null;
|
|
const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base;
|
|
const pathPart = service.healthPath ?? '/health';
|
|
return `${normalizedBase}${pathPart.startsWith('/') ? '' : '/'}${pathPart}`;
|
|
}
|
|
|
|
async function checkHealth(url: string): Promise<{ status: string; detail: string }> {
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
try {
|
|
const response = await fetch(url, { signal: controller.signal });
|
|
const bodyText = await response.text();
|
|
const statusLabel = response.ok ? 'healthy' : 'unhealthy';
|
|
return { status: statusLabel, detail: `${response.status} ${response.statusText}` };
|
|
} catch (error) {
|
|
const reason = (error as Error).message;
|
|
return { status: 'unreachable', detail: reason };
|
|
} finally {
|
|
clearTimeout(timeout);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const spec = loadSpec();
|
|
const results: Array<{ service: string; target: string; status: string; detail: string }> = [];
|
|
|
|
for (const service of spec.services) {
|
|
const url = buildHealthUrl(service);
|
|
if (!url) {
|
|
results.push({ service: service.key, target: 'N/A', status: 'skipped', detail: 'No target URL defined' });
|
|
continue;
|
|
}
|
|
|
|
const { status, detail } = await checkHealth(url);
|
|
results.push({ service: service.key, target: url, status, detail });
|
|
await delay(50); // small pause to avoid overwhelming endpoints
|
|
}
|
|
|
|
console.table(results);
|
|
|
|
const failures = results.filter((row) => row.status !== 'healthy' && row.status !== 'skipped');
|
|
if (failures.length > 0) {
|
|
console.error(`\nHealth check completed with ${failures.length} issue(s).`);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error('Unexpected error while running health checks', error);
|
|
process.exitCode = 1;
|
|
});
|