import { Module, NestModule, MiddlewareConsumer, OnModuleInit, Logger, } from "@nestjs/common"; import { APP_GUARD } from "@nestjs/core"; import { IamModule } from "./iam/iam.module.js"; import { HealthModule } from "./shared/health/health.module.js"; import { PermissionGuard } from "./middleware/permission.guard.js"; import { AuthMiddleware } from "./middleware/auth.middleware.js"; import { LifecycleService } from "./shared/lifecycle/lifecycle.service.js"; import { OutboxModule } from "@edu/shared-ts/outbox"; import { getDbInstance } from "./config/database.js"; import { getKafkaProducer, connectKafkaProducer, IAM_KAFKA_TOPICS, } from "./config/kafka.js"; /** * IAM 根模块。 * * 装配: * - IamModule(业务) * - HealthModule(健康检查) * - OutboxModule(事务性事件发布,I5 裁决) * - AuthMiddleware(从 Gateway 注入的 x-user-* 头部解析用户身份) * - PermissionGuard(APP_GUARD,DB 驱动 + Redis 缓存,I3 裁决) */ @Module({ imports: [ IamModule, HealthModule, OutboxModule.forRoot({ config: { tableName: "iam_outbox", kafkaTopic: IAM_KAFKA_TOPICS.USER_EVENTS, pollIntervalMs: 1000, batchSize: 20, maxRetryCount: 5, retryBackoffMs: 1000, }, db: getDbInstance(), kafkaProducer: getKafkaProducer(), }), ], providers: [ { provide: APP_GUARD, useClass: PermissionGuard }, LifecycleService, ], }) export class AppModule implements NestModule, OnModuleInit { private readonly logger = new Logger(AppModule.name); async onModuleInit(): Promise { // 连接 Kafka producer(Outbox 投递前置依赖) try { await connectKafkaProducer(); this.logger.log("Kafka producer connected"); } catch (error) { this.logger.error( `Kafka producer connect failed: ${error instanceof Error ? error.message : String(error)}`, ); } } configure(consumer: MiddlewareConsumer): void { // AuthMiddleware 应用于需要鉴权的 /v1/iam 路由 // 公开端点(register/login/refresh/jwks/health/metrics)不走此中间件 consumer .apply(AuthMiddleware) .forRoutes( "v1/iam/me", "v1/iam/logout", "v1/iam/viewports", "v1/iam/permissions/effective", "v1/iam/children", "v1/iam/roles", "v1/iam/permissions", "v1/iam/audit", ); } }