Fix jwt-token
This commit is contained in:
248
server/routes/detectors.js
Normal file
248
server/routes/detectors.js
Normal file
@@ -0,0 +1,248 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const Joi = require('joi');
|
||||
const { validateRequest } = require('../middleware/validation');
|
||||
const { Heartbeat, Device, DroneDetection } = require('../models');
|
||||
const AlertService = require('../services/alertService');
|
||||
const DroneTrackingService = require('../services/droneTrackingService');
|
||||
|
||||
// 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})`);
|
||||
}
|
||||
});
|
||||
|
||||
// Unified schema that accepts both heartbeat and detection payloads
|
||||
const detectorSchema = Joi.object({
|
||||
// Heartbeat fields
|
||||
type: Joi.string().valid('heartbeat').when('device_id', {
|
||||
not: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
key: Joi.string().when('type', {
|
||||
is: 'heartbeat',
|
||||
then: Joi.required()
|
||||
}),
|
||||
|
||||
// Optional heartbeat fields (from original heartbeat route)
|
||||
signal_strength: Joi.number().integer().optional(),
|
||||
battery_level: Joi.number().integer().min(0).max(100).optional(),
|
||||
temperature: Joi.number().optional(),
|
||||
uptime: Joi.number().integer().min(0).optional(),
|
||||
memory_usage: Joi.number().integer().min(0).max(100).optional(),
|
||||
firmware_version: Joi.string().optional(),
|
||||
|
||||
// Detection fields
|
||||
device_id: Joi.number().integer().when('type', {
|
||||
not: 'heartbeat',
|
||||
then: Joi.required()
|
||||
}),
|
||||
geo_lat: Joi.number().min(-90).max(90).when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
geo_lon: Joi.number().min(-180).max(180).when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
device_timestamp: Joi.number().integer().min(0).when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
drone_type: Joi.number().integer().min(0).when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
rssi: Joi.number().when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
freq: Joi.number().when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
drone_id: Joi.number().integer().when('device_id', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.required()
|
||||
}),
|
||||
|
||||
// Optional detection fields
|
||||
confidence_level: Joi.number().min(0).max(1).optional(),
|
||||
signal_duration: Joi.number().integer().min(0).optional()
|
||||
}).or('type', 'device_id'); // Must have either type (heartbeat) or device_id (detection)
|
||||
|
||||
// POST /api/detectors - Unified endpoint for heartbeats and detections
|
||||
router.post('/', validateRequest(detectorSchema), async (req, res) => {
|
||||
try {
|
||||
// Determine if this is a heartbeat or detection based on payload
|
||||
if (req.body.type === 'heartbeat') {
|
||||
return await handleHeartbeat(req, res);
|
||||
} else if (req.body.device_id) {
|
||||
return await handleDetection(req, res);
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Invalid payload: must be either heartbeat or detection format'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in detectors endpoint:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Internal server error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle heartbeat payload
|
||||
async function handleHeartbeat(req, res) {
|
||||
const { type, key, device_id, ...heartbeatData } = req.body;
|
||||
|
||||
console.log(`💓 Heartbeat received from device key: ${key}`);
|
||||
|
||||
// If device_id is not provided, try to find device by key
|
||||
let deviceId = device_id;
|
||||
if (!deviceId) {
|
||||
// Try to extract device ID from key or use key as identifier
|
||||
const keyMatch = key.match(/device[_-]?(\d+)/i);
|
||||
deviceId = keyMatch ? parseInt(keyMatch[1]) : key.hashCode();
|
||||
}
|
||||
|
||||
// Ensure device exists or create it
|
||||
const [device] = await Device.findOrCreate({
|
||||
where: { id: deviceId },
|
||||
defaults: {
|
||||
id: deviceId,
|
||||
name: `Device ${deviceId}`,
|
||||
last_heartbeat: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
// Update device's last heartbeat
|
||||
await device.update({ last_heartbeat: new Date() });
|
||||
|
||||
// Create heartbeat record with all optional fields
|
||||
const heartbeat = await Heartbeat.create({
|
||||
device_id: deviceId,
|
||||
device_key: key,
|
||||
...heartbeatData,
|
||||
received_at: new Date()
|
||||
});
|
||||
|
||||
// Emit real-time update via Socket.IO (from original heartbeat route)
|
||||
req.io.emit('device_heartbeat', {
|
||||
device_id: deviceId,
|
||||
device_key: key,
|
||||
timestamp: heartbeat.received_at,
|
||||
status: 'online',
|
||||
...heartbeatData
|
||||
});
|
||||
|
||||
console.log(`✅ Heartbeat recorded for device ${deviceId}`);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: heartbeat,
|
||||
message: 'Heartbeat recorded successfully'
|
||||
});
|
||||
}
|
||||
|
||||
// Handle detection payload
|
||||
async function handleDetection(req, res) {
|
||||
const detectionData = req.body;
|
||||
|
||||
console.log(`🚁 Drone detection received from device ${detectionData.device_id}: drone_id=${detectionData.drone_id}, type=${detectionData.drone_type}, rssi=${detectionData.rssi}`);
|
||||
|
||||
// Ensure device exists or create it (from original detection route)
|
||||
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 detection record
|
||||
const detection = await DroneDetection.create({
|
||||
...detectionData,
|
||||
server_timestamp: new Date()
|
||||
});
|
||||
|
||||
// Process detection through tracking service for movement analysis (from original)
|
||||
const movementAnalysis = droneTracker.processDetection({
|
||||
...detectionData,
|
||||
server_timestamp: detection.server_timestamp
|
||||
});
|
||||
|
||||
// Emit real-time update via Socket.IO with movement analysis (from original)
|
||||
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 (from original)
|
||||
alertService.processAlert(detection, req.io).catch(error => {
|
||||
console.error('Alert processing error:', error);
|
||||
});
|
||||
|
||||
console.log(`✅ Detection recorded and alert processing initiated for detection ${detection.id}`);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
...detection.toJSON(),
|
||||
movement_analysis: movementAnalysis
|
||||
},
|
||||
message: 'Drone detection recorded successfully'
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function for string hashing (if key is not numeric)
|
||||
String.prototype.hashCode = function() {
|
||||
let hash = 0;
|
||||
if (this.length === 0) return hash;
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
const char = this.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash);
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user