// services/api/src/app-cn.module.ts // 根模块:Prisma(@Global,封装 @photo/db-cn)、日志(nestjs-pino / pino-http)、 // request-id 中间件、全局异常过滤器、全局校验管道(Task 13 起,DTO 经 // class-validator 声明、ValidationPipe 强制,失败由过滤器映射 400 VALIDATION_FAILED)、 // /v1/health、/v1/auth/*(Task 13)。 // 功能模块(albums/uploads/photos/public)自 Task 15+ 起逐个加入。 // 过滤器/管道经 APP_FILTER/APP_PIPE、中间件经 NestModule 在模块层注册——e2e 直接 // import 本模块即可获得与生产一致的管线(全局前缀除外,由入口设置)。 import { type MiddlewareConsumer, Module, type NestModule, ValidationPipe, } from '@nestjs/common'; import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { LoggerModule } from 'nestjs-pino'; import type { IncomingMessage, ServerResponse } from 'node:http'; import { AlbumsModule } from './albums/albums.module'; import { AllExceptionsFilter } from './all-exceptions.filter'; import { AuthModule } from './auth/auth.module'; import { HealthController } from './health.controller'; import { PrismaModule } from './prisma/prisma.module'; import { RequestIdMiddleware, resolveRequestId } from './request-id.middleware'; import { UploadsModule } from './uploads/uploads.module'; import { resolveLogLevel } from './shared/log-level'; import type { ServerRequestWithId } from './shared/request-with-id'; @Module({ imports: [ // @Global:功能模块(Task 12+)直接注入 PrismaService,无需 re-import。 PrismaModule, AuthModule, // 相册域(Task 14 起步:PermissionsService;控制器自 Task 15 加入)。 AlbumsModule, // 上传域(Task 18 init;Task 19 renew/complete/history)。 UploadsModule, LoggerModule.forRoot({ pinoHttp: { // 与 RequestIdMiddleware 共用同一解析函数:优先透传 x-request-id, // 其次复用已生成的 req.id,否则生成 UUID;并回写响应头。 genReqId: (req: IncomingMessage, res: ServerResponse) => resolveRequestId(req as ServerRequestWithId, res), redact: { // 授权头 + Cookie(会话凭据,Task 7 遗留项③):请求/响应两侧都打码, // 防止日志聚合层留存可重放的凭据。 paths: [ 'req.headers.authorization', 'req.headers.cookie', 'res.headers.authorization', 'res.headers.set-cookie', ], censor: '[REDACTED]', }, // Task 7 遗留项⑤:非法 LOG_LEVEL 在模块装配期(即启动期)快速失败, // 而不是让 pino 首次写日志时才炸或静默吞掉输出。 level: resolveLogLevel(process.env.LOG_LEVEL), }, }), ], controllers: [HealthController], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, // 全局校验管道(Task 13):whitelist 剥离 DTO 未声明的属性(契约未禁未知 // 字段,剥离而非拒绝);校验失败抛 BadRequestException → 过滤器映射 // 400 VALIDATION_FAILED。经 APP_PIPE 在模块层注册,e2e 复用同一管线。 { provide: APP_PIPE, useFactory: () => new ValidationPipe({ whitelist: true }), }, ], }) export class AppCnModule implements NestModule { configure(consumer: MiddlewareConsumer): void { consumer.apply(RequestIdMiddleware).forRoutes('*'); } }