const express = require('express'); const router = express.Router(); const Joi = require('joi'); const { DroneDetection, Device } = require('../models'); const { Op } = require('sequelize'); const AlertService = require('../services/alertService'); const DroneTrackingService = require('../services/droneTrackingService'); const { validateRequest } = require('../middleware/validation'); // Initialize services const alertService = new AlertService(); const droneTracker = new DroneTrackingService(); // Handle movement alerts from the tracking service droneTracker.on('movement_alert', (alertData) => { const { io } = require('../index'); if (io) { // Emit to dashboard with detailed movement information io.emitToDashboard('drone_movement_alert', { ...alertData, timestamp: new Date().toISOString() }); // Emit to specific device room io.emitToDevice(alertData.deviceId, 'drone_movement_alert', { ...alertData, timestamp: new Date().toISOString() }); console.log(`🚨 Movement Alert: ${alertData.analysis.description} (Drone ${alertData.droneId})`); } }); // Validation schema for drone detection const droneDetectionSchema = Joi.object({ device_id: Joi.number().integer().required(), geo_lat: Joi.number().min(-90).max(90).default(0), geo_lon: Joi.number().min(-180).max(180).default(0), device_timestamp: Joi.number().integer().min(0).default(0), drone_type: Joi.number().integer().min(0).default(0), rssi: Joi.number().integer().default(0), freq: Joi.number().integer().required(), drone_id: Joi.number().integer().required(), confidence_level: Joi.number().min(0).max(1).optional(), signal_duration: Joi.number().integer().min(0).optional() }); // POST /api/detections - Receive drone detection data router.post('/', validateRequest(droneDetectionSchema), async (req, res) => { try { const detectionData = req.body; // Ensure device exists or create it const [device] = await Device.findOrCreate({ where: { id: detectionData.device_id }, defaults: { id: detectionData.device_id, geo_lat: detectionData.geo_lat || 0, geo_lon: detectionData.geo_lon || 0, last_heartbeat: new Date() } }); // Create the detection record const detection = await DroneDetection.create({ ...detectionData, server_timestamp: new Date() }); // Process detection through tracking service for movement analysis const movementAnalysis = droneTracker.processDetection({ ...detectionData, server_timestamp: detection.server_timestamp }); // Emit real-time update via Socket.IO with movement analysis req.io.emit('drone_detection', { id: detection.id, device_id: detection.device_id, drone_id: detection.drone_id, drone_type: detection.drone_type, rssi: detection.rssi, freq: detection.freq, geo_lat: detection.geo_lat, geo_lon: detection.geo_lon, server_timestamp: detection.server_timestamp, confidence_level: detection.confidence_level, signal_duration: detection.signal_duration, movement_analysis: movementAnalysis, device: { id: device.id, name: device.name, geo_lat: device.geo_lat, geo_lon: device.geo_lon } }); // Process alerts asynchronously alertService.processAlert(detection).catch(error => { console.error('Alert processing error:', error); }); res.status(201).json({ success: true, data: detection, message: 'Drone detection recorded successfully' }); } catch (error) { console.error('Error creating drone detection:', error); res.status(500).json({ success: false, message: 'Failed to record drone detection', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); // GET /api/detections - Get drone detections with filtering router.get('/', async (req, res) => { try { const { device_id, drone_id, start_date, end_date, limit = 100, offset = 0, order = 'DESC' } = req.query; const whereClause = {}; if (device_id) whereClause.device_id = device_id; if (drone_id) whereClause.drone_id = drone_id; if (start_date || end_date) { whereClause.server_timestamp = {}; if (start_date) whereClause.server_timestamp[Op.gte] = new Date(start_date); if (end_date) whereClause.server_timestamp[Op.lte] = new Date(end_date); } const detections = await DroneDetection.findAndCountAll({ where: whereClause, include: [{ model: Device, as: 'device', attributes: ['id', 'name', 'geo_lat', 'geo_lon', 'location_description'] }], limit: Math.min(parseInt(limit), 1000), // Max 1000 records offset: parseInt(offset), order: [['server_timestamp', order]] }); res.json({ success: true, data: detections.rows, pagination: { total: detections.count, limit: parseInt(limit), offset: parseInt(offset), pages: Math.ceil(detections.count / parseInt(limit)) } }); } catch (error) { console.error('Error fetching drone detections:', error); res.status(500).json({ success: false, message: 'Failed to fetch drone detections', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); // GET /api/detections/stats - Get detection statistics router.get('/stats', async (req, res) => { try { const { device_id, hours = 24 } = req.query; const whereClause = { server_timestamp: { [Op.gte]: new Date(Date.now() - hours * 60 * 60 * 1000) } }; if (device_id) whereClause.device_id = device_id; const [totalDetections, uniqueDrones, uniqueDevices, avgRssi] = await Promise.all([ DroneDetection.count({ where: whereClause }), DroneDetection.count({ where: whereClause, distinct: true, col: 'drone_id' }), DroneDetection.count({ where: whereClause, distinct: true, col: 'device_id' }), DroneDetection.findAll({ where: whereClause, attributes: [ [sequelize.fn('AVG', sequelize.col('rssi')), 'avg_rssi'] ] }) ]); res.json({ success: true, data: { total_detections: totalDetections, unique_drones: uniqueDrones, active_devices: uniqueDevices, average_rssi: Math.round(avgRssi[0]?.dataValues?.avg_rssi || 0), time_period_hours: hours } }); } catch (error) { console.error('Error fetching detection stats:', error); res.status(500).json({ success: false, message: 'Failed to fetch detection statistics', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); // GET /api/detections/:id - Get specific detection router.get('/:id', async (req, res) => { try { const detection = await DroneDetection.findByPk(req.params.id, { include: [{ model: Device, as: 'device', attributes: ['id', 'name', 'geo_lat', 'geo_lon', 'location_description'] }] }); if (!detection) { return res.status(404).json({ success: false, message: 'Detection not found' }); } res.json({ success: true, data: detection }); } catch (error) { console.error('Error fetching detection:', error); res.status(500).json({ success: false, message: 'Failed to fetch detection', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); // GET /api/detections/tracking/active - Get active drone tracking information router.get('/tracking/active', async (req, res) => { try { const activeTracking = droneTracker.getAllActiveTracking(); res.json({ success: true, data: { active_drones: activeTracking.length, tracking_data: activeTracking } }); } catch (error) { console.error('Error fetching active tracking:', error); res.status(500).json({ success: false, message: 'Failed to fetch active tracking data', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); // GET /api/detections/tracking/:droneId/:deviceId - Get specific drone tracking router.get('/tracking/:droneId/:deviceId', async (req, res) => { try { const { droneId, deviceId } = req.params; const trackingData = droneTracker.getDroneStatus(droneId, deviceId); if (!trackingData) { return res.status(404).json({ success: false, message: 'No tracking data found for this drone-device combination' }); } res.json({ success: true, data: trackingData }); } catch (error) { console.error('Error fetching drone tracking:', error); res.status(500).json({ success: false, message: 'Failed to fetch drone tracking data', error: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); module.exports = router;