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;