feat(security): production bootstrap hardening
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"passport": "^0.7.0",
|
||||
"passport-discord": "^0.1.4",
|
||||
"helmet": "^8.1.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"pg": "^8.16.3",
|
||||
|
||||
34
src/main.ts
34
src/main.ts
@@ -2,6 +2,7 @@ import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import helmet from 'helmet';
|
||||
import { AppModule } from './app.module';
|
||||
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';
|
||||
import { RedisIoAdapter } from './ws/redis-io.adapter';
|
||||
@@ -10,12 +11,28 @@ async function bootstrap() {
|
||||
const logger = new Logger('Bootstrap');
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const configService = app.get(ConfigService);
|
||||
const nodeEnv = configService.get<string>('NODE_ENV') || 'development';
|
||||
const isProduction = nodeEnv === 'production';
|
||||
|
||||
app.enableShutdownHooks();
|
||||
|
||||
app.use(
|
||||
helmet({
|
||||
contentSecurityPolicy: false,
|
||||
crossOriginEmbedderPolicy: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// Configure Redis Adapter for horizontal scaling (if enabled)
|
||||
const redisIoAdapter = new RedisIoAdapter(app, configService);
|
||||
await redisIoAdapter.connectToRedis();
|
||||
app.useWebSocketAdapter(redisIoAdapter);
|
||||
|
||||
app.enableCors({
|
||||
origin: true,
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
// Enable global exception filter for consistent error responses
|
||||
app.useGlobalFilters(new AllExceptionsFilter());
|
||||
|
||||
@@ -29,11 +46,11 @@ async function bootstrap() {
|
||||
// Automatically transform payloads to DTO instances
|
||||
transform: true,
|
||||
// Provide detailed error messages
|
||||
disableErrorMessages: false,
|
||||
disableErrorMessages: isProduction,
|
||||
}),
|
||||
);
|
||||
|
||||
// Configure Swagger documentation
|
||||
if (!isProduction) {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Friendolls API')
|
||||
.setDescription(
|
||||
@@ -59,13 +76,24 @@ async function bootstrap() {
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api', app, document);
|
||||
}
|
||||
|
||||
const host = process.env.HOST ?? 'localhost';
|
||||
const port = process.env.PORT ?? 3000;
|
||||
await app.listen(port);
|
||||
const httpServer = app.getHttpServer() as {
|
||||
once?: (event: 'close', listener: () => void) => void;
|
||||
} | null;
|
||||
httpServer?.once?.('close', () => {
|
||||
void redisIoAdapter.close();
|
||||
});
|
||||
|
||||
logger.log(`Application is running on: http://${host}:${port}`);
|
||||
logger.log(`Swagger documentation available at: http://${host}:${port}/api`);
|
||||
if (!isProduction) {
|
||||
logger.log(
|
||||
`Swagger documentation available at: http://${host}:${port}/api`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user