Files

110 lines
3.8 KiB
JavaScript

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const path = require('path');
const connectDB = require('./config/db');
const authRoutes = require('./routes/authRoutes');
const apiRoutes = require('./routes/apiRoutes');
// Khởi động Image Processing Worker
require('./routes/imageWorker');
// Connect to Database
connectDB();
const app = express();
// Standard middlewares
// Chuẩn bị danh sách các origin được phép cho CORS
const primarySystemHost = process.env.SYSTEM_HOST || 'http://localhost:5000';
let configuredAllowedOrigins = [];
// Thêm SYSTEM_HOST chính
try {
configuredAllowedOrigins.push(new URL(primarySystemHost).origin);
} catch (e) {
console.warn(`[CORS Config Warning] Malformed SYSTEM_HOST: ${primarySystemHost}. Using as-is.`);
configuredAllowedOrigins.push(primarySystemHost);
}
// Thêm các origin bổ sung từ biến môi trường ADDITIONAL_ALLOWED_ORIGINS (cách nhau bởi dấu phẩy)
if (process.env.ADDITIONAL_ALLOWED_ORIGINS) {
process.env.ADDITIONAL_ALLOWED_ORIGINS.split(',').forEach(originStr => {
try {
configuredAllowedOrigins.push(new URL(originStr.trim()).origin);
} catch (e) {
console.warn(`[CORS Config Warning] Malformed origin in ADDITIONAL_ALLOWED_ORIGINS: ${originStr.trim()}. Skipping.`);
}
});
}
const corsOptions = {
origin: function (origin, callback) {
// Cho phép các request không có origin (như Postman hoặc khi render phía server)
if (!origin) return callback(null, true);
let incomingOrigin;
try {
incomingOrigin = new URL(origin).origin;
} catch (e) {
incomingOrigin = origin;
}
// Kiểm tra nếu incomingOrigin nằm trong danh sách các origin được cấu hình
if (configuredAllowedOrigins.includes(incomingOrigin)) return callback(null, true);
// Trong môi trường dev, cho phép các biến thể localhost
const isLocal = incomingOrigin.includes('localhost') || incomingOrigin.includes('127.0.0.1') || incomingOrigin.includes('::1');
if (process.env.NODE_ENV !== 'production' && isLocal) {
return callback(null, true);
}
console.warn(`[CORS Blocked]: Origin ${origin} is not allowed by configuration.`);
callback(new Error('Not allowed by CORS'));
},
credentials: true,
maxAge: 86400 // Cho phép trình duyệt cache kết quả preflight OPTIONS trong 24 giờ
};
app.use(cors(corsOptions));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Request Logger Middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`);
});
next();
});
// API Routes
app.use('/api/auth', authRoutes);
app.use('/api', apiRoutes);
// Serve Frontend static assets from the parent/frontend directory
app.use(express.static(path.join(__dirname, '../frontend')));
// Fallback to index.html for single-page style behaviors
app.use((req, res) => {
res.sendFile(path.join(__dirname, '../frontend/index.html'));
});
// Centralized JSON Error Handler (Ngăn chặn lỗi trả về HTML làm hỏng Frontend)
app.use((err, req, res, next) => {
console.error(`[Error Handler]: ${err.message}`);
res.status(err.status || 500).json({
message: err.message || 'Internal Server Error'
});
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server is running in ${process.env.NODE_ENV || 'development'} mode on port ${PORT}`);
console.log(`CORS Allowed Origins: ${configuredAllowedOrigins.join(', ')}`);
});
// ... cuối file server.js
module.exports = app;