import { NestFactory } from '@nestjs/core'; import { Logger, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import helmet from 'helmet'; import compression from 'compression'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; // import * as path from 'path'; // import * as express from 'express'; 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('APP_HOST') ?? configService.get('HOST') ?? '0.0.0.0'; const port = configService.get('APP_PORT') ?? Number(process.env.APP_PORT ?? 3301); const corsOrigin = configService.get('APP_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, }); // const imageRoot = path.resolve( // configService.get('IMAGE_ROOT_PATH') || '/data/box-images', // ); // app.use( // '/images', // express.static(imageRoot, { // setHeaders: (res: any) => { // res.setHeader('Access-Control-Allow-Origin', corsOrigin); // res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); // res.setHeader( // 'Access-Control-Allow-Headers', // 'Origin, X-Requested-With, Content-Type, Accept, Authorization', // ); // }, // }), // ); // πŸ‘‡ Important: this makes /health become /api/v1/health app.setGlobalPrefix('api/v1', { exclude: ['/'], }); app.use(helmet()); app.use(compression()); app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, transformOptions: { enableImplicitConversion: true, }, forbidNonWhitelisted: false, }), ); // Setup Swagger (OpenAPI) const swaggerConfig = new DocumentBuilder() .setTitle('盒子应用ζŽ₯口文摣') .setDescription( 'box-app-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-app-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-app-api', error); process.exit(1); });