Files
pos-system/microservices/services/_template_nodejs/src/main.ts
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

135 lines
3.6 KiB
TypeScript

import { logger } from '@goodgo/logger';
import { initTracing } from '@goodgo/tracing';
import cors from 'cors';
import express from 'express';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
import { RedisStore } from 'rate-limit-redis';
import { appConfig } from './config/app.config';
import { connectDatabase } from './config/database.config';
import { prisma } from './config/database.config';
import { getRedisClient } from './config/redis.config';
import { setupSwagger } from './docs/swagger';
import { correlationMiddleware } from './middlewares/correlation.middleware';
import { errorHandler, notFoundHandler } from './middlewares/error.middleware';
import { requestLogger } from './middlewares/logger.middleware';
import { metricsMiddleware } from './middlewares/metrics.middleware';
import { createRouter } from './routes';
// EN: Initialize tracing
// VI: Khởi tạo tracing
if (process.env.TRACING_ENABLED === 'true') {
initTracing({
serviceName: process.env.SERVICE_NAME || 'microservice',
otlpEndpoint: process.env.OTLP_ENDPOINT,
enabled: true,
});
}
const app = express();
// EN: Security middleware
// VI: Middleware bảo mật
app.use(helmet());
app.use(
cors({
origin: appConfig.corsOrigin,
credentials: true,
})
);
// EN: Rate limiting
// VI: Giới hạn số lượng request
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
// EN: Use Redis for distributed rate limiting
// VI: Sử dụng Redis để giới hạn rate phân tán
store: new RedisStore({
// @ts-expect-error - rate-limit-redis types mismatch with ioredis
sendCommand: (...args: string[]) => getRedisClient().call(...args),
}),
});
app.use('/api', limiter);
// EN: Correlation ID middleware (must be early)
// VI: Correlation ID middleware (phải đặt sớm)
app.use(correlationMiddleware());
// EN: Body parsing
// VI: Phân tích body request
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// EN: Request logging
// VI: Ghi log request
app.use(requestLogger);
// EN: Metrics
// VI: Metrics
app.use(metricsMiddleware);
// EN: Routes with async error handling
// VI: Routes với async error handling
app.use(createRouter());
// EN: Setup Swagger documentation
// VI: Thiết lập tài liệu Swagger
setupSwagger(app, '/api-docs');
// EN: Error handling
// VI: Xử lý lỗi
app.use(notFoundHandler);
app.use(errorHandler);
const startServer = async () => {
try {
await connectDatabase();
const server = app.listen(appConfig.port, () => {
logger.info(`Service started on port ${appConfig.port}`, {
port: appConfig.port,
nodeEnv: appConfig.nodeEnv,
});
});
// EN: Graceful shutdown
// VI: Đóng ứng dụng một cách an toàn
const shutdown = async (signal: string) => {
logger.info(`${signal} received, shutting down gracefully`);
server.close(async () => {
logger.info('HTTP server closed');
try {
await prisma.$disconnect();
logger.info('Database connection closed');
process.exit(0);
} catch (error) {
logger.error('Error during shutdown', { error });
process.exit(1);
}
});
// EN: Force shutdown after 10s
// VI: Buộc dừng sau 10 giây
setTimeout(() => {
logger.error('Forcing shutdown after timeout');
process.exit(1);
}, 10000);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
} catch (error) {
logger.error('Failed to start server', { error });
process.exit(1);
}
};
startServer();