canina/backend/src/main.ts

122 lines
3.7 KiB
TypeScript

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { join } from 'path';
import { ValidationPipe, BadRequestException } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { CustomHttpExceptionFilter } from './common/filters/http-exception.filter';
import { PrismaExceptionFilter } from './common/filters/prisma-exception.filter';
import { DecimalInterceptor } from './common/interceptors/decimal.interceptor';
import helmet from 'helmet';
async function bootstrap() {
// Fallback defaults for development environment
if (process.env.NODE_ENV !== 'production') {
process.env.JWT_ACCESS_SECRET =
process.env.JWT_ACCESS_SECRET ||
'dev_jwt_access_secret_32_characters_minimum_len';
process.env.JWT_REFRESH_SECRET =
process.env.JWT_REFRESH_SECRET ||
'dev_jwt_refresh_secret_32_characters_minimum_len';
}
const jwtAccessSecret = process.env.JWT_ACCESS_SECRET;
const jwtRefreshSecret = process.env.JWT_REFRESH_SECRET;
if (!jwtAccessSecret || jwtAccessSecret.trim().length < 32) {
console.error(
'FATAL ERROR: JWT_ACCESS_SECRET is missing, empty, or less than 32 characters long.',
);
process.exit(1);
}
if (!jwtRefreshSecret || jwtRefreshSecret.trim().length < 32) {
console.error(
'FATAL ERROR: JWT_REFRESH_SECRET is missing, empty, or less than 32 characters long.',
);
process.exit(1);
}
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// Serve static uploads folder
app.useStaticAssets(join(process.cwd(), 'uploads'), {
prefix: '/uploads/',
});
app.use(
helmet({
contentSecurityPolicy: false, // Avoid blocking Swagger UI scripts and assets
}),
);
app.setGlobalPrefix('api');
app.enableCors({
origin: true,
credentials: true,
});
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
exceptionFactory: (errors) => {
const formattedDetails = errors.map((err) => {
const constraints = err.constraints
? Object.values(err.constraints)
: [];
return {
field: err.property,
message:
constraints.length > 0
? constraints[0]
: 'مقدار وارد شده نامعتبر است.',
};
});
return new BadRequestException({
message:
'اطلاعات ورودی فرم معتبر نمی‌باشد. لطفاً موارد مشخص شده را اصلاح نمایید.',
error: 'INVALID_INPUT_DATA',
details: formattedDetails,
});
},
}),
);
app.useGlobalFilters(
new CustomHttpExceptionFilter(),
new PrismaExceptionFilter(),
);
app.useGlobalInterceptors(new DecimalInterceptor());
const isProduction = process.env.NODE_ENV === 'production';
const enableSwagger = process.env.ENABLE_SWAGGER === 'true';
if (!isProduction || enableSwagger) {
const config = new DocumentBuilder()
.setTitle('Canina Iran API')
.setDescription(
'API Documentation for Canina Iran Pet Health & Supplement Platform',
)
.setVersion('1.0.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
console.log('Swagger UI is ACTIVE on /api/docs');
} else {
console.log('Swagger UI is DISABLED for security (production environment)');
}
await app.listen(process.env.PORT ?? 4001);
}
bootstrap().catch((err: unknown) => {
console.error('Bootstrap error:', err);
process.exit(1);
});