require('dotenv').config({ path: require('path').join(__dirname, '../.env') }); const express = require('express'); const session = require('express-session'); const helmet = require('helmet'); const cors = require('cors'); const logger = require('./config/logger'); const sessionConfig = require('./config/session'); const { testConnection } = require('./config/database'); const errorHandler = require('./middlewares/errorHandler'); const { apiLimiter } = require('./middlewares/rateLimiter'); const app = express(); const PORT = process.env.PORT || 3000; // Security middleware app.use(helmet()); // Dynamic CORS configuration (will be updated from settings) let corsOptions = { origin: process.env.FRONTEND_URL || 'http://localhost:5173', credentials: true, }; app.use((req, res, next) => { cors(corsOptions)(req, res, next); }); // Function to update CORS from database const updateCorsFromSettings = async () => { try { const { Settings } = require('./models'); const corsEnabledSetting = await Settings.findOne({ where: { key: 'cors_enabled' } }); const frontendUrlSetting = await Settings.findOne({ where: { key: 'frontend_url' } }); if (corsEnabledSetting && corsEnabledSetting.value === 'true' && frontendUrlSetting) { corsOptions.origin = frontendUrlSetting.value; logger.info(`CORS enabled for: ${frontendUrlSetting.value}`); } else { // Default: allow both frontend and backend on same origin corsOptions.origin = process.env.FRONTEND_URL || 'http://localhost:5173'; } } catch (error) { logger.warn('Could not load CORS settings from database, using defaults'); } }; // Update CORS on startup (with delay to ensure DB is ready) setTimeout(updateCorsFromSettings, 2000); // Export for use in settings controller app.updateCorsSettings = updateCorsFromSettings; // Body parsing middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Serve static files (landing page and frontend build) const path = require('path'); app.use(express.static(path.join(__dirname, 'public'))); // Serve landing page at /landing route app.get('/landing', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'landing.html')); }); // Session middleware app.use(session(sessionConfig)); // Rate limiting app.use('/api', apiLimiter); // Request logging app.use((req, res, next) => { logger.info(`${req.method} ${req.path}`, { ip: req.ip, userAgent: req.get('user-agent'), }); next(); }); // Health check app.get('/health', (req, res) => { res.json({ success: true, message: 'Server is running', timestamp: new Date().toISOString(), }); }); // API Routes app.use('/api/auth', require('./routes/auth.routes')); app.use('/api/companies', require('./routes/company.routes')); app.use('/api/tokens', require('./routes/token.routes')); app.use('/api/templates', require('./routes/template.routes')); app.use('/api/settings', require('./routes/settings.routes')); app.use('/api/ollama', require('./routes/ollama.routes')); app.use('/api/stats', require('./routes/stats.routes')); // Public tracking route (no rate limit on this specific route) app.use('/t', require('./routes/tracking.routes')); // Serve frontend SPA (must be after API routes) app.get('*', (req, res, next) => { // Skip if it's an API route, tracking route, or landing page if (req.path.startsWith('/api') || req.path.startsWith('/t') || req.path.startsWith('/landing') || req.path.startsWith('/health')) { return next(); } // Serve frontend index.html for all other routes const frontendPath = path.join(__dirname, 'public', 'dist', 'index.html'); res.sendFile(frontendPath, (err) => { if (err) { // If frontend build doesn't exist, return 404 next(); } }); }); // 404 handler app.use((req, res) => { res.status(404).json({ success: false, error: 'Endpoint not found', }); }); // Error handler (must be last) app.use(errorHandler); // Start server const startServer = async () => { try { // Test database connection await testConnection(); // Start listening app.listen(PORT, () => { logger.info(`šŸš€ Server is running on port ${PORT}`); logger.info(`šŸ“Š Environment: ${process.env.NODE_ENV || 'development'}`); logger.info(`šŸ”— Health check: http://localhost:${PORT}/health`); console.log(`\n✨ Oltalama Backend Server Started!`); console.log(`🌐 API: http://localhost:${PORT}/api`); console.log(`šŸŽÆ Tracking: http://localhost:${PORT}/t/:token\n`); }); } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } }; startServer(); module.exports = app;