42 lines
1.7 KiB
JavaScript
42 lines
1.7 KiB
JavaScript
import net from 'node:net';
|
|
const SLOW_MS = 3000;
|
|
|
|
export function classify({ ok, reachable, latency, error }) {
|
|
if (ok) return { status: latency > SLOW_MS ? 'warn' : 'ok', latency_ms: latency, detail: `${latency}ms` };
|
|
if (reachable) return { status: 'warn', latency_ms: latency ?? null, detail: 'degraded' };
|
|
return { status: 'down', latency_ms: null, detail: error || 'unreachable' };
|
|
}
|
|
|
|
// Default probe: HTTP (status 2xx/3xx) or TCP connect. Only called with
|
|
// operator-configured URLs from the registry — never user input.
|
|
export async function probe(svc) {
|
|
const started = Date.now();
|
|
const type = svc.check?.type || 'http';
|
|
try {
|
|
if (type === 'tcp') {
|
|
const u = new URL(svc.url);
|
|
await new Promise((resolve, reject) => {
|
|
const sock = net.connect({ host: u.hostname, port: Number(u.port) }, () => { sock.end(); resolve(); });
|
|
sock.setTimeout(5000); sock.on('timeout', () => { sock.destroy(); reject(new Error('timeout')); });
|
|
sock.on('error', reject);
|
|
});
|
|
return { ok: true, latency: Date.now() - started };
|
|
}
|
|
const base = svc.url.replace(/\/$/, '');
|
|
const url = base + (svc.check?.path || '');
|
|
const res = await fetch(url, { redirect: 'manual', signal: AbortSignal.timeout(6000) });
|
|
const reachable = true;
|
|
const ok = res.status >= 200 && res.status < 400;
|
|
return { ok, reachable, latency: Date.now() - started };
|
|
} catch (e) {
|
|
return { ok: false, reachable: false, latency: Date.now() - started, error: e.code || e.message };
|
|
}
|
|
}
|
|
|
|
export async function checkAll(services, probeFn = probe) {
|
|
return Promise.all(services.map(async svc => {
|
|
const c = classify(await probeFn(svc));
|
|
return { service_id: svc.id, ...c };
|
|
}));
|
|
}
|