import type { FastifyInstance } from "fastify"; import os from "node:os"; import { pingDb } from "@spinza/database"; import { CryptoRng, runSpin } from "@spinza/game-core"; import { GAMES } from "@spinza/games"; import { pingRedis, redis } from "../lib/redis"; import { config } from "../config"; import { maintenance } from "../lib/settings"; const startedAt = Date.now(); export async function healthRoutes(app: FastifyInstance) { app.get("/api/health", async () => ({ status: "ok", service: "spinza-api", version: config.version, uptime: Math.round((Date.now() - startedAt) / 1000), node: os.hostname(), maintenance: maintenance().enabled, time: new Date().toISOString(), })); app.get("/api/health/database", async (reply) => { try { const latency = await pingDb(); return { status: "ok", latencyMs: latency }; } catch (e) { return { status: "down", error: (e as Error).message }; } }); app.get("/api/health/redis", async () => { try { const latency = await pingRedis(); return { status: "ok", latencyMs: latency }; } catch (e) { return { status: "down", error: (e as Error).message }; } }); app.get("/api/health/game-engine", async () => { const t = Date.now(); const def = GAMES[0]; const out = runSpin(def, 100, { rng: new CryptoRng() }); return { status: "ok", games: GAMES.length, sampleGame: def.slug, steps: out.steps.length, latencyMs: Date.now() - t }; }); app.get("/api/ready", async (_req, reply) => { try { await Promise.all([pingDb(), pingRedis()]); return { ready: true }; } catch (e) { reply.code(503); return { ready: false, error: (e as Error).message }; } }); app.get("/api/live", async () => { const cutoff = Date.now() - 5 * 60_000; const players = await redis().zcount("live:players", cutoff, "+inf"); return { players }; }); }