Files
flxn-app/src/lib/telemetry/health.server.ts
T
kyle 06fe1f4ed7 feat(telemetry): rollups, alerts, scheduler, health checks, admin server fns
- pure rollup accumulators with tests; daily upserts keyed date/metric/dim
- alert checks with db-backed cooldown and push to admin players
- in-process scheduler started from health probes
- health endpoint checks pb + supertokens, stale-while-revalidate, always 200
- sse connection counter moved to emitter
- telemetry feature server fns: events/errors/groups/rollups/alerts/status
2026-08-25 21:38:08 -07:00

57 lines
1.5 KiB
TypeScript

export interface HealthChecks {
pocketbase: "ok" | "down";
supertokens: "ok" | "down";
}
export interface HealthSnapshot {
status: "ok" | "degraded";
checks: HealthChecks;
checkedAt: string;
}
const CHECK_TTL_MS = 10_000;
const CHECK_TIMEOUT_MS = 2_000;
let cached: HealthSnapshot | null = null;
let refreshing: Promise<HealthSnapshot> | null = null;
const ping = async (url: string): Promise<boolean> => {
try {
const response = await fetch(url, { signal: AbortSignal.timeout(CHECK_TIMEOUT_MS) });
return response.ok;
} catch {
return false;
}
};
const runChecks = async (): Promise<HealthSnapshot> => {
const [pocketbase, supertokens] = await Promise.all([
ping(`${process.env.POCKETBASE_URL}/api/health`),
ping(`${process.env.SUPERTOKENS_URI}/hello`),
]);
cached = {
status: pocketbase && supertokens ? "ok" : "degraded",
checks: {
pocketbase: pocketbase ? "ok" : "down",
supertokens: supertokens ? "ok" : "down",
},
checkedAt: new Date().toISOString(),
};
return cached;
};
// Stale-while-revalidate: k8s probes get the cached snapshot instantly while a
// refresh runs in the background; only the very first request awaits a check.
export const getHealthSnapshot = async (): Promise<HealthSnapshot> => {
if (cached && Date.now() - Date.parse(cached.checkedAt) < CHECK_TTL_MS) {
return cached;
}
if (!refreshing) {
refreshing = runChecks().finally(() => {
refreshing = null;
});
}
return cached ?? refreshing;
};