// Load environment variables require('dotenv').config(); const http = require('http'); const fs = require('fs'); const path = require('path'); // Route handlers const departuresRouter = require('./server/routes/departures'); const sitesRouter = require('./server/routes/sites'); const configRouter = require('./server/routes/config'); const PORT = process.env.PORT || 3002; // Default configuration let config = { sites: [ { id: '1411', name: 'Ambassaderna', enabled: true } ] }; // Function to load configuration from file function loadSitesConfig() { try { const fs = require('fs'); const configPath = path.join('config', 'sites.json'); if (fs.existsSync(configPath)) { const configData = fs.readFileSync(configPath, 'utf8'); const loadedConfig = JSON.parse(configData); // Handle old format (array of sites) if (Array.isArray(loadedConfig)) { config.sites = loadedConfig; } else { config = loadedConfig; } console.log('Loaded configuration:', config); } } catch (error) { console.error('Error loading configuration:', error); } } // Load configuration on startup loadSitesConfig(); // Create HTTP server const server = http.createServer(async (req, res) => { const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } // Handle API endpoints - use route handlers if (parsedUrl.pathname === '/api/departures') { await departuresRouter.handleDepartures(req, res, config); } else if (parsedUrl.pathname === '/api/sites/search') { sitesRouter.handleSiteSearch(req, res, parsedUrl); } else if (parsedUrl.pathname === '/api/sites/nearby') { await sitesRouter.handleNearbySites(req, res, parsedUrl); } else if (parsedUrl.pathname === '/api/config') { configRouter.handleGetConfig(req, res, config); } else if (parsedUrl.pathname === '/api/config/update' && req.method === 'POST') { configRouter.handleUpdateConfig(req, res, config); } else if (parsedUrl.pathname === '/api/config/client') { configRouter.handleClientConfig(req, res); } // Serve static files else if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/index.html') { fs.readFile('index.html', 'utf8', (err, data) => { if (err) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error loading index.html'); return; } // Always inject API key and config into HTML (even if empty, so window vars exist) const apiKey = process.env.OPENWEATHERMAP_API_KEY || ''; if (!data.includes('window.OPENWEATHERMAP_API_KEY')) { // Inject before closing tag const configScript = ` `; data = data.replace('', configScript + '\n'); } res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(data); }); } // Serve static files from public directory or root else if (/\.(js|css|png|jpg|jpeg|gif|ico|svg|json)$/.test(parsedUrl.pathname)) { let filePath = parsedUrl.pathname.substring(1); // Remove leading / // Try public directory first, then root directory for backward compatibility const publicPath = path.join('public', filePath); const rootPath = filePath; // Check if file exists in public directory first fs.access(publicPath, fs.constants.F_OK, (publicErr) => { const targetPath = publicErr ? rootPath : publicPath; fs.readFile(targetPath, (err, data) => { if (err) { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('File not found'); return; } const ext = parsedUrl.pathname.split('.').pop(); const contentType = { 'js': 'text/javascript', 'css': 'text/css', 'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'ico': 'image/x-icon', 'svg': 'image/svg+xml', 'json': 'application/json' }[ext] || 'text/plain'; res.writeHead(200, { 'Content-Type': contentType }); res.end(data); }); }); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); } }); // Start the server server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}/`); });