import { NestFactory } from '@nestjs/core'; import { Logger, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule, { bufferLogs: true, }); const logger = new Logger('Bootstrap'); app.useLogger(logger); const configService = app.get(ConfigService); const host = configService.get('STATS_HOST') ?? configService.get('HOST') ?? '0.0.0.0'; const port = configService.get('STATS_PORT') ?? Number(process.env.STATS_PORT ?? 3302); const corsOrigin = configService.get('STATS_CORS_ORIGIN') ?? '*'; const corsOriginOption = corsOrigin === '*' ? true : corsOrigin .split(',') .map((o) => o.trim()) .filter(Boolean); app.enableCors({ origin: corsOriginOption, methods: 'GET,PUT,PATCH,POST,DELETE', credentials: true, }); // 👇 Important: this makes /health become /api/v1/health app.setGlobalPrefix('api/v1', { exclude: ['/'], }); app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, transformOptions: { enableImplicitConversion: true, }, forbidNonWhitelisted: false, }), ); // Setup Swagger (OpenAPI) const swaggerConfig = new DocumentBuilder() .setTitle('盒子统计接口文档') .setDescription( 'box-stats-api 的公开接口文档,面向前端应用和统计服务。包含用户登录历史等模块。', ) .setVersion('1.0.0') .build(); const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup('api-docs', app, swaggerDocument, { jsonDocumentUrl: '/api-docs-json', swaggerOptions: { persistAuthorization: true, docExpansion: 'none', }, }); // Prevent Swagger UI/JSON from being cached (browser + proxies) app.use(['/api-docs', '/api-docs-json'], (req, res, next) => { res.setHeader( 'Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate', ); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0'); next(); }); await app.listen(port, host); const url = `http://${host}:${port}`; logger.log(`🚀 box-stats-api listening on ${url} (global prefix: /api/v1)`); logger.log(`📖 Swagger 文档: ${url}/api-docs`); logger.log(`📄 Swagger JSON: ${url}/api-docs-json`); } bootstrap().catch((error) => { // eslint-disable-next-line no-console console.error('❌ Failed to bootstrap box-stats-api', error); process.exit(1); });