import cluster from 'node:cluster'; import path from 'node:path'; import { HttpStatus, Logger, UnprocessableEntityException, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { NestFactory } from '@nestjs/core'; import { NestFastifyApplication } from '@nestjs/platform-fastify'; import { useContainer } from 'class-validator'; import { AppModule } from './app.module'; import { fastifyApp } from './common/adapters/fastify.adapter'; import { RedisIoAdapter } from './common/adapters/socket.adapter'; import { LoggingInterceptor } from './common/interceptors/logging.interceptor'; import type { ConfigKeyPaths } from './config'; import { isDev, isMainProcess } from './global/env'; import { setupSwagger } from './setup-swagger'; import { LoggerService } from './shared/logger/logger.service'; declare const module: any; async function bootstrap() { const app = await NestFactory.create(AppModule, fastifyApp, { bufferLogs: true, snapshot: true, // forceCloseConnections: true, }); const configService = app.get(ConfigService); const { port, globalPrefix } = configService.get('app', { infer: true }); // class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器) useContainer(app.select(AppModule), { fallbackOnErrors: true }); app.enableCors({ origin: '*', credentials: true }); app.setGlobalPrefix(globalPrefix); app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }); // Starts listening for shutdown hooks !isDev && app.enableShutdownHooks(); if (isDev) app.useGlobalInterceptors(new LoggingInterceptor()); app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true, transformOptions: { enableImplicitConversion: true }, // forbidNonWhitelisted: true, // 禁止 无装饰器验证的数据通过 errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, stopAtFirstError: true, exceptionFactory: errors => new UnprocessableEntityException( errors.map(e => { const rule = Object.keys(e.constraints!)[0]; const msg = e.constraints![rule]; return msg; })[0] ), }) ); app.useWebSocketAdapter(new RedisIoAdapter(app)); setupSwagger(app, configService); await app.listen(port, '0.0.0.0', async () => { app.useLogger(app.get(LoggerService)); const url = await app.getUrl(); const { pid } = process; const env = cluster.isPrimary; const prefix = env ? 'P' : 'W'; if (!isMainProcess) return; const logger = new Logger('NestApplication'); logger.log(`[${prefix + pid}] Server running on ${url}`); if (isDev) logger.log(`[${prefix + pid}] OpenAPI: ${url}/api-docs`); }); if (module.hot) { module.hot.accept(); module.hot.dispose(() => app.close()); } } bootstrap();