86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
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<void> {
|
||
// 连接 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",
|
||
);
|
||
}
|
||
}
|