import 'reflect-metadata'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { TransformInterceptor } from './common/interceptors/transform.interceptor'; import { ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import cookieParser from 'cookie-parser'; import { DEV_JWT_SECRET, DEV_JWT_REFRESH_SECRET } from './config/configuration'; export type BackendRuntimeConfig = { nodeEnv: string; jwtSecret: string; jwtRefreshSecret: string; corsOrigins: string[]; }; export function assertSafeProductionConfig(config: BackendRuntimeConfig): void { if (config.nodeEnv !== 'production') return; if (!config.jwtSecret || config.jwtSecret === DEV_JWT_SECRET) { throw new Error('JWT_SECRET must be set to a non-default value in production'); } if (!config.jwtRefreshSecret || config.jwtRefreshSecret === DEV_JWT_REFRESH_SECRET) { throw new Error('JWT_REFRESH_SECRET must be set to a non-default value in production'); } if (config.corsOrigins.length === 0) { throw new Error('BACKEND_CORS_ORIGINS must contain at least one origin in production'); } } export function buildCorsOrigin(nodeEnv: string, corsOrigins: string[]): boolean | string[] { return nodeEnv === 'production' ? corsOrigins : true; } async function bootstrap() { const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api/v1'); app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); app.useGlobalFilters(new HttpExceptionFilter()); app.useGlobalInterceptors(new TransformInterceptor()); app.use(cookieParser()); const configService = app.get(ConfigService); const runtimeConfig: BackendRuntimeConfig = { nodeEnv: process.env.NODE_ENV || 'development', jwtSecret: configService.get('app.auth.jwtSecret', ''), jwtRefreshSecret: configService.get('app.auth.jwtRefreshSecret', ''), corsOrigins: configService.get('app.cors.origins', []), }; assertSafeProductionConfig(runtimeConfig); app.enableCors({ origin: buildCorsOrigin(runtimeConfig.nodeEnv, runtimeConfig.corsOrigins), credentials: true, }); const config = new DocumentBuilder() .setTitle('MoexVibe API') .setVersion('1.0.0') .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api/docs', app, document); const port = process.env.PORT || 3000; await app.listen(port); console.log(`MoexVibe API running on http://localhost:${port}/api/v1`); console.log(`Swagger docs: http://localhost:${port}/api/docs`); } if (process.env.NODE_ENV !== 'test') { void bootstrap(); }