import { NestFactory } from '@nestjs/core'; import { json } from 'express'; import { AppModule } from './app.module'; import cookieParser from 'cookie-parser'; import { ValidationPipe, VersioningType } from '@nestjs/common'; import session from 'express-session'; import { emitGQLSchemaFile } from './gql-schema'; import * as crypto from 'crypto'; import morgan from 'morgan'; import { ConfigService } from '@nestjs/config'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { InfraTokenModule } from './infra-token/infra-token.module'; import { NestExpressApplication } from '@nestjs/platform-express'; function setupSwagger( app: NestExpressApplication, isProduction: boolean, ): void { const swaggerDocPath = '/api-docs'; const config = new DocumentBuilder() .setTitle('Hoppscotch API Documentation') .setDescription('APIs for external integration') .addApiKey( { type: 'apiKey', name: 'Authorization', in: 'header', scheme: 'bearer', bearerFormat: 'Bearer', }, 'infra-token', ) .build(); const document = SwaggerModule.createDocument(app, config, { include: isProduction ? [InfraTokenModule] : [], }); SwaggerModule.setup(swaggerDocPath, app, document, { swaggerOptions: { persistAuthorization: true, ignoreGlobalPrefix: true }, }); } async function bootstrap() { const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); const isProduction = configService.get('PRODUCTION') === 'true'; console.log(`Running in production: ${isProduction}`); console.log(`Port: ${configService.get('PORT')}`); app.use( session({ // Allow overriding the default cookie name 'connect.sid' (which contains a dot). // Some proxies/load balancers (like older Kong versions) cannot hash cookie names with dots, // so we allow setting an alternative name via the INFRA.SESSION_COOKIE_NAME configuration. name: configService.get('INFRA.SESSION_COOKIE_NAME') || 'connect.sid', secret: configService.get('INFRA.SESSION_SECRET') || crypto.randomBytes(16).toString('hex'), resave: false, saveUninitialized: false, }), ); // Increase file upload limit to 100MB app.use( json({ limit: '100mb', }), ); if (isProduction) { console.log('Enabling CORS with production settings'); app.enableCors({ origin: configService.get('WHITELISTED_ORIGINS').split(','), credentials: true, }); } else { console.log('Enabling CORS with development settings'); app.enableCors({ origin: true, credentials: true, }); } app.enableVersioning({ type: VersioningType.URI, }); app.use(cookieParser()); app.useGlobalPipes( new ValidationPipe({ transform: true, }), ); if (configService.get('TRUST_PROXY') === 'true') { console.log('Enabling trust proxy'); app.set('trust proxy', true); } app.use(morgan(':remote-addr :method :url :status - :response-time ms')); await setupSwagger(app, isProduction); await app.listen(configService.get('PORT') || 3170); // Graceful shutdown process.on('SIGTERM', async () => { console.info('SIGTERM signal received, initiating graceful shutdown...'); await app.close(); console.info('Application closed successfully'); process.exit(0); }); } if (!process.env.GENERATE_GQL_SCHEMA) { bootstrap(); } else { emitGQLSchemaFile(); }