const http = require('http'); const https = require('https'); const express = require('express'); const cors = require('cors'); const app = express(); const PORT = process.env.PORT || 3000; app.use(cors()); app.use(express.json()); // In-memory cache for Waze requests (30s TTL per geo tile) const cache = new Map(); const CACHE_TTL_MS = 30 * 1000; // User agents and headers mimicking genuine browser requests const BROWSER_USER_AGENTS = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' ]; function getRandomUserAgent() { return BROWSER_USER_AGENTS[Math.floor(Math.random() * BROWSER_USER_AGENTS.length)]; } /** * Determine Waze server environment based on longitude * na: North America (Western Hemisphere approx) * il: Israel * row: Rest of World */ function getWazeEnv(lat, lng) { if (lat >= 29.0 && lat <= 33.5 && lng >= 34.0 && lng <= 36.0) { return 'il'; } if (lng >= -170.0 && lng <= -50.0) { return 'na'; } return 'row'; } /** * Fetch raw GeoRSS alerts from Waze with browser impersonation */ function fetchWazeAlerts(top, bottom, left, right, env = 'na') { return new Promise((resolve, reject) => { const query = new URLSearchParams({ top: top.toString(), bottom: bottom.toString(), left: left.toString(), right: right.toString(), env: env, types: 'alerts,traffic,jams' }); const url = `https://www.waze.com/live-map/api/georss?${query.toString()}`; const parsed = new URL(url); const req = https.request({ hostname: parsed.hostname, path: parsed.pathname + parsed.search, method: 'GET', headers: { 'User-Agent': getRandomUserAgent(), 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': 'https://www.waze.com/live-map/', 'Sec-Ch-Ua': '"Chromium";v="124", "Google Chrome";v="124"', 'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Platform': '"Windows"', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'Connection': 'keep-alive' }, timeout: 8000 }, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { try { const data = JSON.parse(body); resolve(data); } catch (e) { reject(new Error(`Failed to parse Waze JSON: ${e.message}`)); } } else { reject(new Error(`Waze responded with HTTP ${res.statusCode}: ${body.slice(0, 150)}`)); } }); }); req.on('timeout', () => { req.destroy(); reject(new Error('Waze request timed out')); }); req.on('error', (err) => { reject(err); }); req.end(); }); } /** * Normalize and format Waze raw alerts into MotorSignal Community Schema */ function normalizeAlerts(wazeData) { const rawAlerts = wazeData.alerts || []; const normalized = []; for (const item of rawAlerts) { const lat = item.location?.y ?? item.latitude ?? item.lat; const lng = item.location?.x ?? item.longitude ?? item.lng; if (lat == null || lng == null) continue; const type = (item.type || 'HAZARD').toUpperCase(); const subtype = (item.subtype || item.subType || '').toUpperCase(); const street = item.street || item.road || ''; const city = item.city || ''; let category = 'other'; let description = item.reportDescription || ''; if (type.includes('POLICE') || subtype.includes('POLICE')) { category = 'police'; if (subtype.includes('HIDDEN')) { description = description || 'Hidden police speed trap reported'; } else if (subtype.includes('VISIBLE')) { description = description || 'Police speed trap reported'; } else { description = description || 'Police presence reported'; } } else if (type.includes('SPEED_CAMERA') || subtype.includes('SPEED_CAMERA') || type.includes('CAMERA')) { category = 'speed_camera'; description = description || 'Speed enforcement camera'; } else if (type.includes('RED_LIGHT_CAMERA') || subtype.includes('RED_LIGHT_CAMERA')) { category = 'speed_camera'; description = description || 'Red light camera'; } else if (type.includes('ACCIDENT') || subtype.includes('ACCIDENT')) { category = 'accident'; description = description || (subtype.includes('MAJOR') ? 'Major collision' : 'Accident reported'); } else if (type.includes('HAZARD') || subtype.includes('HAZARD') || subtype.includes('WEATHER') || subtype.includes('DEBRIS')) { category = 'hazard'; description = description || (subtype.includes('POT_HOLE') ? 'Pothole on roadway' : 'Road hazard / debris'); } else if (type.includes('ROAD_CLOSED') || subtype.includes('ROAD_CLOSED')) { category = 'road_closure'; description = description || 'Road closed'; } else if (type.includes('CONSTRUCTION') || subtype.includes('CONSTRUCTION')) { category = 'construction'; description = description || 'Road work / Construction'; } else if (type.includes('JAM') || type.includes('TRAFFIC')) { category = 'traffic'; description = description || 'Heavy traffic congestion'; } if (street) { description += ` on ${street}`; } normalized.push({ id: item.uuid || item.id || `wz_${lat.toFixed(5)}_${lng.toFixed(5)}`, providerId: 'community_traffic', source: 'Waze Community Relay', type: type, subtype: subtype, category: category, latitude: parseFloat(lat), longitude: parseFloat(lng), heading: item.magvar || item.heading || null, roadName: street, city: city, description: description, confidence: (item.confidence != null ? item.confidence / 10.0 : (item.reportRating != null ? item.reportRating / 5.0 : 0.85)), severity: item.reportRating >= 4 ? 'high' : (item.reportRating >= 2 ? 'medium' : 'low'), reliability: item.reliability || 5, reportRating: item.reportRating || 3, nThumbsUp: item.nThumbsUp || 0, pubMillis: item.pubMillis || Date.now(), reportedAt: new Date(item.pubMillis || Date.now()).toISOString() }); } return normalized; } // ------------------------------------------------------------- // REST API ROUTES // ------------------------------------------------------------- /** * Health check & discovery endpoint (Supports MotorSignal TEST CONNECTION button) */ app.get(['/', '/health', '/test'], (req, res) => { res.json({ status: 'ok', service: 'MotorSignal Waze Community Relay', version: '1.0.0', timestamp: new Date().toISOString(), uptimeSeconds: Math.floor(process.uptime()), features: ['police', 'speed_cameras', 'hazards', 'accidents', 'traffic'] }); }); /** * Main alerts endpoint */ app.get(['/alerts', '/api/alerts'], async (req, res) => { try { let top, bottom, left, right, lat, lng; if (req.query.top && req.query.bottom && req.query.left && req.query.right) { top = parseFloat(req.query.top); bottom = parseFloat(req.query.bottom); left = parseFloat(req.query.left); right = parseFloat(req.query.right); lat = (top + bottom) / 2; lng = (left + right) / 2; } else if (req.query.lat && req.query.lng) { lat = parseFloat(req.query.lat); lng = parseFloat(req.query.lng); const radiusMeters = parseFloat(req.query.radius || 5000); const latDelta = radiusMeters / 111000.0; const lngDelta = radiusMeters / (111000.0 * Math.cos(lat * Math.PI / 180.0)); top = lat + latDelta; bottom = lat - latDelta; left = lng - lngDelta; right = lng + lngDelta; } else { lat = 37.7749; lng = -122.4194; top = 37.82; bottom = 37.72; left = -122.48; right = -122.36; } const env = req.query.env || getWazeEnv(lat, lng); const cacheKey = `${lat.toFixed(2)}_${lng.toFixed(2)}_${env}`; const cached = cache.get(cacheKey); if (cached && (Date.now() - cached.time < CACHE_TTL_MS)) { return res.json({ provider: 'waze_relay', cached: true, count: cached.alerts.length, alerts: cached.alerts }); } try { const wazeRaw = await fetchWazeAlerts(top, bottom, left, right, env); const normalizedAlerts = normalizeAlerts(wazeRaw); cache.set(cacheKey, { time: Date.now(), alerts: normalizedAlerts }); return res.json({ provider: 'waze_relay', cached: false, count: normalizedAlerts.length, alerts: normalizedAlerts }); } catch (fetchErr) { console.warn(`[WazeRelay] Warning: ${fetchErr.message}`); if (cached) { return res.json({ provider: 'waze_relay', cached: true, stale: true, count: cached.alerts.length, alerts: cached.alerts }); } return res.json({ provider: 'waze_relay', status: 'partial', message: fetchErr.message, count: 0, alerts: [] }); } } catch (err) { console.error('[WazeRelay] Server Error:', err); res.status(500).json({ error: err.message, alerts: [] }); } }); app.listen(PORT, '0.0.0.0', () => { console.log(`====================================================`); console.log(` 🏍️ MotorSignal Waze Alert Relay Server running`); console.log(` 📡 Listening on: http://0.0.0.0:${PORT}`); console.log(` 🏥 Healthcheck: http://localhost:${PORT}/health`); console.log(` 🚨 Alerts API: http://localhost:${PORT}/alerts?lat=37.77&lng=-122.42`); console.log(`====================================================`); });