import { Controller, Get, HttpException, HttpStatus } from "@nestjs/common"; import { sql } from "drizzle-orm"; import { getDb } from "../../config/database.js"; import { getRedis } from "../../config/redis.js"; import { getJwtKeyPair } from "../../config/jwt.js"; const SERVICE_NAME = "iam"; interface DependencyCheck { name: string; status: "ok" | "error"; error?: string; } /** * 健康检查端点。 * * - GET /healthz:liveness,仅返回进程存活,不检查依赖 * - GET /readyz:readiness,检查 5 依赖(DB/Redis/Kafka/gRPC/JWKS),失败返回 503 */ @Controller() export class HealthController { @Get("healthz") liveness(): { status: string; service: string; timestamp: string } { return { status: "ok", service: SERVICE_NAME, timestamp: new Date().toISOString(), }; } @Get("readyz") async readiness(): Promise<{ status: string; service: string; timestamp: string; dependencies: DependencyCheck[]; }> { const checks: DependencyCheck[] = []; // 1. DB checks.push(await this.checkDb()); // 2. Redis checks.push(await this.checkRedis()); // 3. Kafka(检查 producer 连接状态——通过 ping) checks.push(await this.checkKafka()); // 4. JWKS(检查密钥文件已加载) checks.push(this.checkJwks()); // 5. gRPC(本进程内启动,进程存活即 gRPC 存活) checks.push({ name: "grpc", status: "ok" }); const allOk = checks.every((c) => c.status === "ok"); if (!allOk) { throw new HttpException( { status: "error", service: SERVICE_NAME, timestamp: new Date().toISOString(), dependencies: checks, }, HttpStatus.SERVICE_UNAVAILABLE, ); } return { status: "ok", service: SERVICE_NAME, timestamp: new Date().toISOString(), dependencies: checks, }; } private async checkDb(): Promise { try { const db = getDb(); await db.execute(sql`SELECT 1`); return { name: "database", status: "ok" }; } catch (error) { return { name: "database", status: "error", error: error instanceof Error ? error.message : String(error), }; } } private async checkRedis(): Promise { try { const redis = getRedis(); const pong = await redis.ping(); if (pong !== "PONG") { return { name: "redis", status: "error", error: `Unexpected: ${pong}` }; } return { name: "redis", status: "ok" }; } catch (error) { return { name: "redis", status: "error", error: error instanceof Error ? error.message : String(error), }; } } private async checkKafka(): Promise { try { // Kafka producer 连接状态由 AppModule.onModuleInit 建立 // 这里仅检查 producer 实例是否可用 const { getKafkaProducer } = await import("../../config/kafka.js"); const producer = getKafkaProducer(); void producer; // 实例存在即视为可用 return { name: "kafka", status: "ok" }; } catch (error) { return { name: "kafka", status: "error", error: error instanceof Error ? error.message : String(error), }; } } private checkJwks(): DependencyCheck { try { getJwtKeyPair(); return { name: "jwks", status: "ok" }; } catch (error) { return { name: "jwks", status: "error", error: error instanceof Error ? error.message : String(error), }; } } }