135 lines
3.6 KiB
TypeScript
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();
|