forked from Shared/esg
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
import './telemetry';
|
|
import { Logger, ValidationPipe } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { NestFactory } from '@nestjs/core';
|
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
import { NextFunction, Request, Response } from 'express';
|
|
import { AppModule } from './app.module';
|
|
import { API_KEY_HEADER } from './common/constants/app.constants';
|
|
import { OpenTelemetryNestLogger } from './otel-nest-logger';
|
|
import { buildValidationErrorResponse } from './common/helpers/validation-error.helper';
|
|
import { AppException } from './common/exceptions/app-exception';
|
|
import {
|
|
recordHttpRequestEnd,
|
|
recordHttpRequestStart,
|
|
shutdownTelemetry,
|
|
} from './telemetry';
|
|
|
|
async function bootstrap(): Promise<void> {
|
|
const logger = new OpenTelemetryNestLogger();
|
|
Logger.overrideLogger(logger);
|
|
|
|
const app = await NestFactory.create(AppModule, { logger });
|
|
const configService = app.get(ConfigService);
|
|
const port = configService.get<number>('app.port', 8085);
|
|
|
|
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
const start = Date.now();
|
|
const attributes = {
|
|
'http.request.method': req.method,
|
|
'http.route': req.path,
|
|
};
|
|
|
|
recordHttpRequestStart(attributes);
|
|
|
|
res.on('finish', () => {
|
|
recordHttpRequestEnd(Date.now() - start, {
|
|
...attributes,
|
|
'http.response.status_code': res.statusCode,
|
|
});
|
|
});
|
|
|
|
next();
|
|
});
|
|
|
|
app.useGlobalPipes(
|
|
new ValidationPipe({
|
|
whitelist: true,
|
|
forbidNonWhitelisted: true,
|
|
transform: true,
|
|
transformOptions: { enableImplicitConversion: true },
|
|
exceptionFactory: (errors) => {
|
|
const { normalizedError } = buildValidationErrorResponse(errors);
|
|
return new AppException('VALIDATION_ERROR', {
|
|
message: normalizedError.message,
|
|
messageFa: normalizedError.messageFa,
|
|
details: normalizedError.details,
|
|
});
|
|
},
|
|
}),
|
|
);
|
|
|
|
const baseUrlProd = process.env.BASE_URL_PROD;
|
|
|
|
const documentBuilder = new DocumentBuilder()
|
|
.setTitle('External Services Gateway')
|
|
.setDescription(
|
|
'Unified gateway for external inquiry providers with JWT authentication, RBAC, ' +
|
|
'per-user inquiry access, audit logging, and normalized inquiry responses.',
|
|
)
|
|
.setVersion('1.0')
|
|
.addServer("http://localhost:8085")
|
|
.addServer("https://apex.mic.co.ir/esg/api");
|
|
|
|
if (baseUrlProd) {
|
|
documentBuilder.addServer(baseUrlProd);
|
|
}
|
|
|
|
const swaggerConfig = documentBuilder
|
|
.addBearerAuth(
|
|
{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT', in: 'header' },
|
|
'bearer',
|
|
)
|
|
.addApiKey({ type: 'apiKey', name: API_KEY_HEADER, in: 'header' }, 'api-key')
|
|
.build();
|
|
|
|
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
|
SwaggerModule.setup('api/docs', app, document);
|
|
|
|
await app.listen(port);
|
|
Logger.log(`Inquiry Gateway running on http://localhost:${port}`);
|
|
Logger.log(`Swagger docs: http://localhost:${port}/api/docs`);
|
|
|
|
const shutdown = async (signal: NodeJS.Signals): Promise<void> => {
|
|
Logger.log(`Received ${signal}, shutting down`);
|
|
await app.close();
|
|
await shutdownTelemetry();
|
|
process.exit(0);
|
|
};
|
|
|
|
process.once('SIGINT', shutdown);
|
|
process.once('SIGTERM', shutdown);
|
|
}
|
|
|
|
bootstrap().catch(async (error: unknown) => {
|
|
Logger.error('Application failed to start', error instanceof Error ? error.stack : String(error));
|
|
await shutdownTelemetry();
|
|
process.exit(1);
|
|
});
|