import { NestFactory } from "@nestjs/core"; import { ValidationPipe, Logger as NestLogger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; import { AppModule } from "./app.module"; import helmet from "helmet"; import * as express from "express"; import { Logger, LoggerErrorInterceptor } from "nestjs-pino"; import { SanitizeInterceptor } from "./common/interceptors/sanitize.interceptor"; // BigInt serialization polyfill — Prisma returns BigInt for mstUtc etc. (BigInt.prototype as unknown as { toJSON: () => string }).toJSON = function () { return this.toString(); }; async function bootstrap() { const logger = new NestLogger("Bootstrap"); logger.log("🔄 Starting application..."); const app = await NestFactory.create(AppModule, { bufferLogs: false }); // Use Pino Logger app.useLogger(app.get(Logger)); app.useGlobalInterceptors( new LoggerErrorInterceptor(), new SanitizeInterceptor(), ); // Security Headers app.use(helmet()); // Request payload size limit app.use(express.json({ limit: "1mb" })); app.use(express.urlencoded({ extended: true, limit: "1mb" })); // Graceful Shutdown (Prisma & Docker) app.enableShutdownHooks(); // Get config service const configService = app.get(ConfigService); const port = configService.get("PORT", 3005); const nodeEnv = configService.get("NODE_ENV", "development"); // Enable CORS app.enableCors({ origin: nodeEnv === "production" ? [ "https://ui-suggestbet.bilgich.com", "https://suggestbet.bilgich.com", "https://iddaai.com", "https://www.iddaai.com", "http://localhost:6195", ] : true, credentials: true, }); // Global prefix app.setGlobalPrefix("api"); // Validation pipe (Strict) app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true, transformOptions: { enableImplicitConversion: true, }, }), ); // Swagger setup — hidden in production if (nodeEnv !== "production") { const swaggerConfig = new DocumentBuilder() .setTitle("Suggest-Bet API") .setDescription( "AI-driven sports betting prediction engine with smart coupon generation", ) .setVersion("1.0") .addBearerAuth() .addTag("Auth", "Authentication endpoints") .addTag("Users", "User management endpoints") .addTag("Admin", "Admin management endpoints") .addTag("Health", "Health check endpoints") .addTag("Matches", "Match listing and detail endpoints") .addTag("Leagues", "League, country, and team discovery endpoints") .addTag("Analysis", "AI analysis and analysis history endpoints") .addTag("Coupon", "Coupon generation and coupon management endpoints") .addTag("Predictions", "Prediction and smart-coupon endpoints") .build(); logger.log("Initializing Swagger..."); const document = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup("api/docs", app, document, { swaggerOptions: { persistAuthorization: true, }, }); logger.log("Swagger initialized"); } logger.log(`Attempting to listen on port ${port}...`); await app.listen(port, "0.0.0.0"); logger.log("═══════════════════════════════════════════════════════════"); logger.log(`🚀 Server is running on: http://localhost:${port}/api`); logger.log(`📚 Swagger documentation: http://localhost:${port}/api/docs`); logger.log(`💚 Health check: http://localhost:${port}/api/health`); logger.log(`🌍 Environment: ${nodeEnv.toUpperCase()}`); logger.log("═══════════════════════════════════════════════════════════"); if (nodeEnv === "development") { logger.warn("⚠️ Running in development mode"); } } void bootstrap();