Files
Edu/services/iam/src/shared/health/health.controller.ts
SpecialX a35e759d64 feat(iam): 完整实现 iam 身份认证与权限服务
包含 jwt/jwks/audit/grpc、rbac、cache、redis/kafka 配置等完整实现
2026-07-10 19:09:39 +08:00

138 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 /healthzliveness仅返回进程存活不检查依赖
* - GET /readyzreadiness检查 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<DependencyCheck> {
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<DependencyCheck> {
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<DependencyCheck> {
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),
};
}
}
}