122 lines
3.7 KiB
TypeScript
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);
|
|
});
|