require('dotenv').config(); const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const path = require('path'); const http = require('http'); // Initialize Express app const app = express(); // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], }, }, crossOriginEmbedderPolicy: false, // Needed for some static assets })); app.use(cors({ origin: process.env.NODE_ENV === 'production' ? [process.env.FRONTEND_URL] : ['http://localhost:3000', 'http://localhost:19000'], credentials: true })); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again later.' }); app.use(limiter); // Body parsing middleware app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Static files app.use(express.static(path.join(__dirname, 'public'))); // Tenant resolution and isolation middleware const { resolveTenant, enforceTenantIsolation } = require('./middleware/tenant'); app.use(resolveTenant); app.use(enforceTenantIsolation); // Import and use routes const authRoutes = require('./routes/auth'); const jobSeekerRoutes = require('./routes/jobSeeker'); const jobProviderRoutes = require('./routes/jobProvider'); const tenantRoutes = require('./routes/tenant'); app.use('/api/auth', authRoutes); app.use('/api/job-seekers', jobSeekerRoutes); app.use('/api/job-providers', jobProviderRoutes); app.use('/api/tenants', tenantRoutes); // Basic route app.get('/', (req, res) => { res.json({ message: 'Welcome to MerchantsOfHope.org - TSYS Group Recruiting Platform', status: 'running', timestamp: new Date().toISOString(), tenantId: req.tenantId }); }); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), service: 'MOH Portal API', tenantId: req.tenantId }); }); // Error handling middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ error: 'Something went wrong!', message: process.env.NODE_ENV === 'development' ? err.message : 'Internal server error', tenantId: req.tenantId }); }); // 404 handler app.use('*', (req, res) => { res.status(404).json({ error: 'Route not found', tenantId: req.tenantId }); }); module.exports = app; // Only start the server if this file is run directly (not imported for testing) if (require.main === module) { const server = http.createServer(app); const PORT = process.env.PORT || 19000; server.listen(PORT, () => { console.log(`MerchantsOfHope.org server running on port ${PORT}`); console.log(`Tenant identification enabled - using tenant: default or from request`); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM received, shutting down gracefully'); server.close(() => { console.log('Process terminated'); }); }); process.on('SIGINT', () => { console.log('SIGINT received, shutting down gracefully'); server.close(() => { console.log('Process terminated'); }); }); }