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()