import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import type { NestExpressApplication } from '@nestjs/platform-express'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { randomUUID } from 'crypto'; import { existsSync } from 'fs'; import { join } from 'path'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ResponseInterceptor } from './common/interceptors/response.interceptor'; import type { RequestWithContext } from './common/interfaces/request-with-context.interface'; import { PrismaService } from './prisma/prisma.service'; async function bootstrap() { const app = await NestFactory.create(AppModule); const prisma = app.get(PrismaService); app.use((req: RequestWithContext, res, next) => { req.traceId = req.headers['x-trace-id']?.toString() ?? randomUUID(); res.setHeader('x-trace-id', req.traceId); next(); }); app.setGlobalPrefix('api/v1'); app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, transformOptions: { enableImplicitConversion: true, }, }), ); app.useGlobalInterceptors(new ResponseInterceptor()); app.useGlobalFilters(new HttpExceptionFilter()); const swaggerConfig = new DocumentBuilder() .setTitle('ToolsShow Backend API') .setDescription('NestJS backend for ToolsShow with hybrid web/download tool access.') .setVersion('1.0.0') .addBearerAuth( { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', }, 'admin-access-token', ) .build(); const document = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup('api/docs', app, document); const clientDistPath = process.env.CLIENT_DIST_PATH ?? join(__dirname, '..', 'public'); if (existsSync(clientDistPath)) { app.useStaticAssets(clientDistPath); const expressApp = app.getHttpAdapter().getInstance(); expressApp.get(/^(?!\/api(?:\/|$)).*/, (_req, res) => { res.sendFile(join(clientDistPath, 'index.html')); }); } await prisma.enableShutdownHooks(app); await app.listen(process.env.PORT ?? 3000); } bootstrap();