yiekheng 4a790b9a60 feat(bot): scaffold env, logger, db, health, shutdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:10:37 +08:00

52 lines
1.4 KiB
TypeScript

import { createServer, type Server } from "node:http";
import { sql } from "drizzle-orm";
import { db } from "./db.js";
import { env } from "./env.js";
import { logger } from "./logger.js";
export type HealthStatus = {
ok: boolean;
uptimeSec: number;
db: "ok" | "error";
sessions?: Record<string, number>;
};
const started = Date.now();
let getSessionCounts: () => Record<string, number> = () => ({});
export function setSessionCountsProvider(fn: () => Record<string, number>): void {
getSessionCounts = fn;
}
export async function buildHealth(): Promise<HealthStatus> {
let dbStatus: "ok" | "error" = "ok";
try {
await db.execute(sql`select 1`);
} catch (err) {
logger.warn({ err }, "health: db ping failed");
dbStatus = "error";
}
return {
ok: dbStatus === "ok",
uptimeSec: Math.round((Date.now() - started) / 1000),
db: dbStatus,
sessions: getSessionCounts(),
};
}
export function startHealthServer(): Server {
const server = createServer(async (req, res) => {
if (req.url !== "/health") {
res.writeHead(404).end("not found");
return;
}
const status = await buildHealth();
res.writeHead(status.ok ? 200 : 503, { "content-type": "application/json" });
res.end(JSON.stringify(status));
});
server.listen(env.BOT_HEALTH_PORT, () => {
logger.info({ port: env.BOT_HEALTH_PORT }, "health server listening");
});
return server;
}