/** * Departures route handler * Handles fetching and returning transit departure data */ const https = require('https'); /** * Fetch departures for a specific site from SL Transport API * @param {string} siteId - The site ID to fetch departures for * @returns {Promise} - Departure data */ function fetchDeparturesForSite(siteId) { return new Promise((resolve, reject) => { const apiUrl = `https://transport.integration.sl.se/v1/sites/${siteId}/departures`; console.log(`Fetching data from: ${apiUrl}`); https.get(apiUrl, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { console.log('Raw API response:', data.substring(0, 200) + '...'); try { try { const parsedData = JSON.parse(data); console.log('Successfully parsed as regular JSON'); resolve(parsedData); return; } catch (jsonError) { console.log('Not valid JSON, trying to fix format...'); } if (data.startsWith('departures":')) { data = '{' + data; } else if (data.includes('departures":')) { const startIndex = data.indexOf('departures":'); if (startIndex > 0) { data = '{' + data.substring(startIndex); } } data = data.replace(/}{\s*"/g, '},{"'); data = data.replace(/"([^"]+)":\s*([^,{}\[\]]+)(?=")/g, '"$1": $2,'); data = data.replace(/,\s*}/g, '}').replace(/,\s*\]/g, ']'); try { const parsedData = JSON.parse(data); console.log('Successfully parsed fixed JSON'); if (parsedData && parsedData.departures && parsedData.departures.length > 0) { console.log('Sample departure structure:', JSON.stringify(parsedData.departures[0], null, 2)); const sample = parsedData.departures[0]; console.log('Direction fields:', { direction: sample.direction, directionText: sample.directionText, directionCode: sample.directionCode, destination: sample.destination }); } resolve(parsedData); } catch (parseError) { console.error('Failed to parse even after fixing:', parseError); // Return empty departures array instead of rejecting to be more resilient resolve({ departures: [], error: 'Failed to parse API response: ' + parseError.message }); } } catch (error) { console.error('Error processing API response:', error); // Return empty departures array instead of rejecting to be more resilient resolve({ departures: [], error: 'Error processing API response: ' + error.message }); } }); }).on('error', (error) => { console.error('Error fetching departures:', error); reject(error); }); }); } /** * Fetch departures for all enabled sites * @param {Array} enabledSites - Array of enabled site configurations * @returns {Promise} - Object with sites array containing departure data */ async function fetchAllDepartures(enabledSites) { if (enabledSites.length === 0) { return { sites: [], error: 'No enabled sites configured' }; } try { const sitesPromises = enabledSites.map(async (site) => { try { const departureData = await fetchDeparturesForSite(site.id); return { siteId: site.id, siteName: site.name, data: departureData }; } catch (error) { console.error(`Error fetching departures for site ${site.id}:`, error); return { siteId: site.id, siteName: site.name, error: error.message }; } }); const results = await Promise.all(sitesPromises); return { sites: results }; } catch (error) { console.error('Error fetching all departures:', error); return { sites: [], error: error.message }; } } /** * Handle departures API endpoint * @param {http.IncomingMessage} req - HTTP request object * @param {http.ServerResponse} res - HTTP response object * @param {Object} config - Application configuration */ async function handleDepartures(req, res, config) { try { const enabledSites = config.sites.filter(site => site.enabled); const data = await fetchAllDepartures(enabledSites); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); } catch (error) { console.error('Error handling departures request:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: error.message })); } } module.exports = { handleDepartures, fetchDeparturesForSite, fetchAllDepartures };