/** * Production Backend Server for India Driver Tracking * GitHub: socketio/socket.io (60k stars), expressjs/express (65k stars) */ const express = require('express'); const { createServer } = require('http'); const { Server } = require('socket.io'); const Redis = require('ioredis'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const rateLimit = require('express-rate-limit'); const app = express(); const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: "*", // Configure for your domain in production methods: ["GET", "POST"] }, transports: ['websocket', 'polling'] // Fallback for Indian mobile networks }); // Redis for real-time location storage (65k stars: redis/redis) const redis = new Redis({ host: process.env.REDIS_HOST || 'localhost', port: process.env.REDIS_PORT || 6379, retryDelayOnFailover: 100 }); // Security middleware app.use(helmet()); app.use(compression()); app.use(cors()); app.use(express.json({ limit: '10mb' })); // Rate limiting for India mobile networks const limiter = rateLimit({ windowMs: 1 * 60 * 1000, // 1 minute max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP' }); app.use('/api/', limiter); // Active drivers storage (in-memory + Redis) const activeDrivers = new Map(); // Socket.IO connection handling io.on('connection', (socket) => { console.log('Client connected:', socket.id); // Driver authentication middleware socket.on('driver:auth', async (data) => { const { driverId, vehicleId, authToken } = data; // Verify token here in production socket.driverId = driverId; socket.vehicleId = vehicleId; socket.isDriver = true; // Store socket mapping await redis.hset(`driver:${driverId}`, 'socketId', socket.id, 'status', 'online'); socket.join(`driver:${driverId}`); socket.join('all-drivers'); console.log(`Driver ${driverId} authenticated`); socket.emit('auth:success', { message: 'Connected to tracking server' }); }); // Admin dashboard authentication socket.on('admin:auth', (data) => { socket.isAdmin = true; socket.join('admins'); // Send current active drivers list const driversList = Array.from(activeDrivers.values()); socket.emit('drivers:list', driversList); }); // Real-time GPS update from driver app socket.on('location:update', async (data) => { if (!socket.isDriver) return; const { lat, lng, accuracy, speed, heading, timestamp } = data; const driverId = socket.driverId; // Validate coordinates (India boundaries roughly) if (lat < 6 || lat > 37 || lng < 68 || lng > 97) { return socket.emit('error', { message: 'Invalid coordinates for India' }); } const locationData = { driverId, vehicleId: socket.vehicleId, lat, lng, accuracy: accuracy || 0, speed: speed || 0, heading: heading || 0, timestamp: timestamp || Date.now(), lastUpdate: Date.now() }; // Update Redis (expires in 5 minutes if no update) await redis.setex(`loc:${driverId}`, 300, JSON.stringify(locationData)); await redis.geoadd('drivers:geo', lng, lat, driverId); // Update memory activeDrivers.set(driverId, locationData); // Broadcast to admins with throttling (every 2 seconds per driver) socket.to('admins').emit('driver:moved', locationData); }); // Handle driver status changes socket.on('status:update', async (data) => { if (!socket.isDriver) return; const { status } = data; // 'available', 'busy', 'offline' await redis.hset(`driver:${socket.driverId}`, 'status', status); io.to('admins').emit('driver:status', { driverId: socket.driverId, status, timestamp: Date.now() }); }); // Admin requesting specific driver history socket.on('admin:track-driver', async (driverId) => { if (!socket.isAdmin) return; socket.join(`track:${driverId}`); const lastLoc = await redis.get(`loc:${driverId}`); if (lastLoc) { socket.emit('driver:location', JSON.parse(lastLoc)); } }); // Handle disconnect socket.on('disconnect', async () => { if (socket.isDriver && socket.driverId) { await redis.hset(`driver:${socket.driverId}`, 'status', 'offline'); await redis.expire(`driver:${socket.driverId}`, 3600); // Keep for 1 hour activeDrivers.delete(socket.driverId); io.to('admins').emit('driver:offline', { driverId: socket.driverId }); } console.log('Client disconnected:', socket.id); }); }); // REST API endpoints for historical data app.get('/api/drivers/active', async (req, res) => { try { const drivers = []; const keys = await redis.keys('loc:*'); for (const key of keys) { const data = await redis.get(key); if (data) drivers.push(JSON.parse(data)); } res.json({ count: drivers.length, drivers }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Get drivers in specific radius (for India city operations) app.get('/api/drivers/nearby', async (req, res) => { const { lat, lng, radius = 10 } = req.query; // radius in km try { const nearby = await redis.georadius( 'drivers:geo', parseFloat(lng), parseFloat(lat), parseFloat(radius), 'km', 'WITHCOORD', 'WITHDIST' ); res.json({ nearby }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', uptime: process.uptime(), timestamp: new Date().toISOString(), region: 'India' }); }); const PORT = process.env.PORT || 3000; httpServer.listen(PORT, () => { console.log(`🚀 Production Tracking Server running on port ${PORT}`); console.log(`📍 Optimized for India GPS tracking`); });