spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1/** /api/health, /api/ready, /api/metrics — no cache, no envelope. */2import type { FastifyInstance } from "fastify";3import { performance, type EventLoopUtilization } from "node:perf_hooks";4import { pg } from "../lib/sql.js";5import { metrics } from "../metrics.js";6import { cacheStats } from "../cache.js";7import { redisPing } from "../redis.js";89export const API_VERSION = "0.1.0";1011export async function systemRoutes(app: FastifyInstance): Promise<void> {12 app.get("/api/health", { schema: { summary: "Liveness (fast, no I/O)", tags: ["system"] } }, async (_req, reply) => {13 reply.header("cache-control", "no-store");14 return { ok: true, service: "dci-api", version: API_VERSION, uptimeSec: Math.round(process.uptime()), now: new Date().toISOString() };15 });1617 app.get("/api/ready", { schema: { summary: "Readiness: Postgres ping (Redis reported, not required)", tags: ["system"] } }, async (_req, reply) => {18 reply.header("cache-control", "no-store");19 const started = Date.now();20 let db = false;21 let dbError: string | null = null;22 try {23 const sql = pg();24 await Promise.race([sql`select 1`, new Promise((_, rej) => setTimeout(() => rej(new Error("db timeout")), 3_000))]);25 db = true;26 } catch (e) {27 dbError = (e as Error).message;28 }29 const redis = await redisPing();30 const body = { ok: db, db, dbError, redis, dbLatencyMs: Date.now() - started, now: new Date().toISOString() };31 return reply.code(db ? 200 : 503).send(body);32 });3334 app.get("/api/metrics", { schema: { summary: "Prometheus metrics", tags: ["system"] } }, async (_req, reply) => {35 const cs = cacheStats();36 const poolMax = Number(process.env.PG_POOL_MAX ?? 16);37 reply.header("cache-control", "no-store");38 reply.type("text/plain; version=0.0.4; charset=utf-8");39 return metrics.render({ dci_api_cache_memory_entries: cs.entries, dci_api_cache_memory_max: cs.max, dci_api_db_pool_max: poolMax, dci_api_process_rss_bytes: process.memoryUsage().rss, dci_api_event_loop_utilization: Math.round(performanceUtilization() * 1000) / 1000 });40 });41}4243let lastElu: EventLoopUtilization | null = null;44function performanceUtilization(): number {45 const cur = lastElu ? performance.eventLoopUtilization(lastElu) : performance.eventLoopUtilization();46 lastElu = performance.eventLoopUtilization();47 return cur.utilization;48}49