const express = require('express'); const { Op } = require('sequelize'); const { DroneDetection, Device } = require('../models'); const { authenticateToken } = require('../middleware/auth'); const { getDroneTypeInfo } = require('../utils/droneTypes'); const router = express.Router(); /** * GET /api/detections * Get all drone detections with filtering and pagination */ router.get('/', authenticateToken, async (req, res) => { try { const { device_id, drone_id, start_date, end_date, page = 1, limit = 50, sort = 'server_timestamp', order = 'desc' } = req.query; // Build where clause for filtering const whereClause = { // Exclude drone type 0 (None) from normal detection queries drone_type: { [Op.ne]: 0 } }; if (device_id) { whereClause.device_id = device_id; } if (drone_id) { whereClause.drone_id = drone_id; } if (start_date) { whereClause.server_timestamp = { ...whereClause.server_timestamp, [Op.gte]: new Date(start_date) }; } if (end_date) { whereClause.server_timestamp = { ...whereClause.server_timestamp, [Op.lte]: new Date(end_date) }; } // Calculate offset for pagination const offset = (parseInt(page) - 1) * parseInt(limit); // Query detections with device information const detections = await DroneDetection.findAll({ where: whereClause, include: [{ model: Device, as: 'device', attributes: ['id', 'name', 'geo_lat', 'geo_lon', 'location_description', 'is_approved'] }], order: [[sort, order.toUpperCase()]], limit: parseInt(limit), offset: offset }); // Get total count for pagination const totalCount = await DroneDetection.count({ where: whereClause }); // Calculate pagination info const totalPages = Math.ceil(totalCount / parseInt(limit)); const hasNextPage = parseInt(page) < totalPages; const hasPrevPage = parseInt(page) > 1; // Enhance detections with drone type information const enhancedDetections = detections.map(detection => { const droneTypeInfo = getDroneTypeInfo(detection.drone_type); return { ...detection.toJSON(), drone_type_info: droneTypeInfo }; }); res.json({ detections: enhancedDetections, pagination: { currentPage: parseInt(page), totalPages, totalCount, limit: parseInt(limit), hasNextPage, hasPrevPage } }); } catch (error) { console.error('Error fetching detections:', error); res.status(500).json({ error: 'Failed to fetch detections', details: error.message }); } }); /** * GET /api/detections/:id * Get a specific detection by ID */ router.get('/:id', authenticateToken, async (req, res) => { try { const { id } = req.params; const detection = await DroneDetection.findByPk(id, { include: [{ model: Device, as: 'device', attributes: ['id', 'name', 'geo_lat', 'geo_lon', 'location_description', 'is_approved'] }] }); if (!detection) { return res.status(404).json({ error: 'Detection not found' }); } // Enhance detection with drone type information const droneTypeInfo = getDroneTypeInfo(detection.drone_type); const enhancedDetection = { ...detection.toJSON(), drone_type_info: droneTypeInfo }; res.json(enhancedDetection); } catch (error) { console.error('Error fetching detection:', error); res.status(500).json({ error: 'Failed to fetch detection', details: error.message }); } }); /** * DELETE /api/detections/:id * Delete a specific detection (admin only) */ router.delete('/:id', authenticateToken, async (req, res) => { try { // Check if user is admin if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Admin access required' }); } const { id } = req.params; const detection = await DroneDetection.findByPk(id); if (!detection) { return res.status(404).json({ error: 'Detection not found' }); } await detection.destroy(); res.json({ message: 'Detection deleted successfully' }); } catch (error) { console.error('Error deleting detection:', error); res.status(500).json({ error: 'Failed to delete detection', details: error.message }); } }); /** * GET /api/detections/debug * Get all detections including drone type 0 (None) for debugging purposes * Admin access only */ router.get('/debug', authenticateToken, async (req, res) => { try { // Check if user is admin if (req.user.role !== 'admin') { return res.status(403).json({ success: false, error: 'Access denied', message: 'Admin access required for debug data' }); } const { device_id, drone_id, drone_type, start_date, end_date, page = 1, limit = 100, sort = 'server_timestamp', order = 'desc' } = req.query; // Build where clause for debugging (includes all drone types) const whereClause = {}; if (device_id) { whereClause.device_id = device_id; } if (drone_id) { whereClause.drone_id = drone_id; } if (drone_type !== undefined) { whereClause.drone_type = parseInt(drone_type); } if (start_date) { whereClause.server_timestamp = { ...whereClause.server_timestamp, [Op.gte]: new Date(start_date) }; } if (end_date) { whereClause.server_timestamp = { ...whereClause.server_timestamp, [Op.lte]: new Date(end_date) }; } // Calculate offset for pagination const offset = (parseInt(page) - 1) * parseInt(limit); // Query ALL detections including type 0 for debugging const detections = await DroneDetection.findAndCountAll({ where: whereClause, include: [{ model: Device, as: 'device', attributes: ['id', 'name', 'location', 'geo_lat', 'geo_lon'] }], limit: parseInt(limit), offset: offset, order: [[sort, order.toUpperCase()]] }); // Add drone type information to each detection const enhancedDetections = detections.rows.map(detection => { const droneTypeInfo = getDroneTypeInfo(detection.drone_type); return { ...detection.toJSON(), drone_type_info: droneTypeInfo, is_debug_data: detection.drone_type === 0 }; }); res.json({ success: true, data: enhancedDetections, pagination: { total: detections.count, page: parseInt(page), limit: parseInt(limit), pages: Math.ceil(detections.count / parseInt(limit)) }, debug_info: { includes_none_detections: true, total_none_detections: await DroneDetection.count({ where: { drone_type: 0 } }), message: "Debug data includes drone type 0 (None) detections" } }); } catch (error) { console.error('Error fetching debug detections:', error); res.status(500).json({ success: false, error: 'Failed to fetch debug detections', details: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error' }); } }); module.exports = router;