api-client/packages/hoppscotch-backend/src/main.ts
Mir Arif Hasan 904a1b0405
chore: security patch for the dependency chain v2025.11.0 (#5590)
Bump dependencies and account for breaking changes.

---------

Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com>
2025-11-24 14:21:29 +05:30

123 lines
3.5 KiB
TypeScript

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<NestExpressApplication>(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<string>('INFRA.SESSION_COOKIE_NAME') || 'connect.sid',
secret:
configService.get<string>('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();
}