Fix jwt-token

This commit is contained in:
2025-08-19 19:13:32 +02:00
parent 4a62b0d5cc
commit 88e2260e98
8 changed files with 593 additions and 37 deletions

5
.env
View File

@@ -17,6 +17,11 @@ TWILIO_ACCOUNT_SID=your_twilio_account_sid_here
TWILIO_AUTH_TOKEN=your_twilio_auth_token_here
TWILIO_PHONE_NUMBER=your_twilio_phone_number_here
# Health Probe Simulator Configuration
PROBE_FAILRATE=30
PROBE_INTERVAL_SECONDS=60
API_BASE_URL=https://selfservice.cqers.com/drones/api
# Optional: Override default settings
# NODE_ENV=production
# CORS_ORIGIN=http://localhost:3000

18
Dockerfile.healthprobe Normal file
View File

@@ -0,0 +1,18 @@
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install required Python packages
RUN pip install --no-cache-dir requests
# Copy the health probe simulator script
COPY health_probe_simulator.py .
# Set environment variables with defaults
ENV PROBE_FAILRATE=30
ENV PROBE_INTERVAL_SECONDS=60
ENV API_BASE_URL=https://selfservice.cqers.com/drones/api
# Run the health probe simulator
CMD ["python", "health_probe_simulator.py"]

View File

@@ -142,6 +142,25 @@ services:
- simulation
command: python drone_simulator.py --devices 5 --duration 3600
# Health Probe Simulator (Continuous Device Heartbeats)
healthprobe:
build:
context: .
dockerfile: Dockerfile.healthprobe
container_name: drone-detection-healthprobe
restart: unless-stopped
environment:
PROBE_FAILRATE: ${PROBE_FAILRATE:-30}
PROBE_INTERVAL_SECONDS: ${PROBE_INTERVAL_SECONDS:-60}
API_BASE_URL: http://backend:3001/api
networks:
- drone-network
depends_on:
backend:
condition: service_healthy
profiles:
- healthprobe
volumes:
postgres_data:
driver: local

View File

@@ -0,0 +1,174 @@
# Health Probe Simulator
The Health Probe Simulator continuously sends heartbeat messages from all devices to test the offline detection feature of the drone detection system.
## Features
- 🔄 **Continuous Monitoring**: Sends heartbeats every minute (configurable)
- 📊 **Configurable Failure Rate**: Simulates device failures to test offline detection
- 🎯 **Real Device Integration**: Uses actual devices from your system
- 📈 **Statistics Reporting**: Shows success/failure rates for each cycle
- 🐳 **Docker Support**: Can run as a container alongside the main system
## Configuration
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `PROBE_FAILRATE` | `30` | Percentage of probes that should fail (0-100) |
| `PROBE_INTERVAL_SECONDS` | `60` | Interval between probe cycles in seconds |
| `API_BASE_URL` | `https://selfservice.cqers.com/drones/api` | API endpoint URL |
### .env File
Add these lines to your `.env` file:
```bash
# Health Probe Simulator Configuration
PROBE_FAILRATE=30
PROBE_INTERVAL_SECONDS=60
API_BASE_URL=https://selfservice.cqers.com/drones/api
```
## Usage
### Option 1: Docker Compose (Recommended)
Start the health probe simulator as a Docker service:
```bash
# Start with health probe simulator
docker-compose --profile healthprobe up -d
# View logs
docker-compose logs -f healthprobe
# Stop the service
docker-compose --profile healthprobe down
```
### Option 2: Local Python Script
Run directly with Python:
```bash
# Linux/Mac
./start-healthprobe.sh
# Windows
start-healthprobe.bat
# Or directly with Python
python3 health_probe_simulator.py
```
### Option 3: Manual Docker Build
```bash
# Build the health probe image
docker build -f Dockerfile.healthprobe -t health-probe-simulator .
# Run the container
docker run -d \
--name health-probe \
--network uamils_drone-network \
-e PROBE_FAILRATE=30 \
-e PROBE_INTERVAL_SECONDS=60 \
-e API_BASE_URL=http://backend:3001/api \
health-probe-simulator
```
## Output Example
```
🏥 DRONE DETECTION SYSTEM - HEALTH PROBE SIMULATOR
============================================================
📋 Configuration:
PROBE_FAILRATE: 30%
PROBE_INTERVAL_SECONDS: 60s
API_BASE_URL: https://selfservice.cqers.com/drones/api
🔧 Health Probe Simulator Configuration:
API URL: https://selfservice.cqers.com/drones/api
Probe Interval: 60 seconds
Failure Rate: 30%
📡 Fetched 4 devices for health monitoring
- Device 1941875381: Arlanda Airport Detector (Arlanda Airport Terminal 2)
- Device 1725834521: Royal Castle Detector (Royal Castle Stockholm)
- Device 1456789123: Muskö Naval Base Detector (Muskö Naval Base)
- Device 1987654321: Gothenburg Port Detector (Port of Gothenburg)
🔄 Starting probe cycle at 2025-08-19 14:30:00
Probing 4 devices...
💓 Device 1941875381: Heartbeat sent successfully (Arlanda Airport Detector - Arlanda Airport Terminal 2)
💔 Simulating probe failure for Device 1725834521 (30% failure rate)
💓 Device 1456789123: Heartbeat sent successfully (Muskö Naval Base Detector - Muskö Naval Base)
💔 Simulating probe failure for Device 1987654321 (30% failure rate)
📊 Probe Cycle Summary:
✅ Successful: 2
❌ Failed: 2
📈 Success Rate: 50.0%
🎯 Target Failure Rate: 30%
⏰ Next cycle in 60 seconds
```
## Testing Offline Detection
The health probe simulator is perfect for testing your system's offline detection capabilities:
1. **Set Failure Rate**: Adjust `PROBE_FAILRATE` to control how many devices appear offline
2. **Monitor Dashboard**: Watch the map view to see devices changing status
3. **Check Alerts**: Verify that offline device alerts are triggered
4. **Test Recovery**: Failed devices will randomly come back online in subsequent cycles
## Use Cases
- **🧪 Testing**: Verify offline detection works correctly
- **🎭 Demo**: Show system behavior during device failures
- **📊 Load Testing**: Generate consistent heartbeat traffic
- **🔧 Development**: Test heartbeat handling and device status logic
## Stopping the Simulator
### Docker Compose
```bash
docker-compose --profile healthprobe down
```
### Local Script
Press `Ctrl+C` to stop the simulator gracefully.
### Docker Container
```bash
docker stop health-probe
docker rm health-probe
```
## Troubleshooting
### Common Issues
1. **Connection Errors**: Check API_BASE_URL and network connectivity
2. **Authentication Errors**: Ensure the API endpoint doesn't require authentication for heartbeats
3. **High CPU Usage**: Increase PROBE_INTERVAL_SECONDS to reduce frequency
### Logs
Check the container logs for detailed output:
```bash
docker-compose logs healthprobe
```
## Integration with Main System
The health probe simulator:
- ✅ Uses the same heartbeat API as real devices
- ✅ Automatically discovers all devices in your system
- ✅ Respects your configured offline detection timeouts
- ✅ Generates realistic device status changes
- ✅ Helps validate your monitoring and alerting systems

187
health_probe_simulator.py Normal file
View File

@@ -0,0 +1,187 @@
#!/usr/bin/env python3
"""
Health Check Probe Simulator
Continuously sends heartbeat probes from all devices to test offline detection
"""
import requests
import time
import random
import os
import json
from datetime import datetime
from typing import List, Dict
class DeviceProbeSimulator:
def __init__(self):
self.api_base_url = os.getenv('API_BASE_URL', 'https://selfservice.cqers.com/drones/api')
self.probe_interval = int(os.getenv('PROBE_INTERVAL_SECONDS', '60')) # 1 minute default
self.probe_failrate = int(os.getenv('PROBE_FAILRATE', '30')) # 30% failure rate
self.devices = []
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'User-Agent': 'HealthProbeSimulator/1.0'
})
print(f"🔧 Health Probe Simulator Configuration:")
print(f" API URL: {self.api_base_url}")
print(f" Probe Interval: {self.probe_interval} seconds")
print(f" Failure Rate: {self.probe_failrate}%")
print()
def fetch_devices(self) -> bool:
"""Fetch all devices from the API"""
try:
response = self.session.get(f"{self.api_base_url}/devices")
if response.status_code == 200:
data = response.json()
if data.get('success') and 'data' in data:
self.devices = data['data']
print(f"📡 Fetched {len(self.devices)} devices for health monitoring")
for device in self.devices:
print(f" - Device {device['id']}: {device.get('name', 'Unnamed')} ({device.get('location_description', 'No location')})")
return True
else:
print(f"❌ API returned invalid data structure: {data}")
return False
else:
print(f"❌ Failed to fetch devices: HTTP {response.status_code}")
print(f" Response: {response.text}")
return False
except Exception as e:
print(f"❌ Error fetching devices: {e}")
return False
def send_heartbeat(self, device: Dict) -> bool:
"""Send heartbeat for a specific device"""
device_id = device['id']
# Simulate probe failure based on failure rate
if random.randint(1, 100) <= self.probe_failrate:
print(f"💔 Simulating probe failure for Device {device_id} ({self.probe_failrate}% failure rate)")
return False
try:
payload = {
'device_id': device_id,
'timestamp': int(time.time()),
'status': 'online',
'system_info': {
'cpu_usage': random.randint(20, 80),
'memory_usage': random.randint(30, 90),
'disk_usage': random.randint(40, 85),
'temperature': random.randint(25, 65),
'uptime': random.randint(3600, 86400) # 1 hour to 1 day
}
}
response = self.session.post(
f"{self.api_base_url}/devices/heartbeat",
json=payload,
timeout=10
)
if response.status_code in [200, 201]:
device_name = device.get('name', f'Device {device_id}')
location = device.get('location_description', 'Unknown location')
print(f"💓 Device {device_id}: Heartbeat sent successfully ({device_name} - {location})")
return True
else:
print(f"❌ Failed to send heartbeat for Device {device_id}: HTTP {response.status_code}")
print(f" Response: {response.text}")
return False
except requests.exceptions.Timeout:
print(f"⏰ Heartbeat timeout for Device {device_id}")
return False
except Exception as e:
print(f"❌ Error sending heartbeat for Device {device_id}: {e}")
return False
def run_probe_cycle(self):
"""Run one complete probe cycle for all devices"""
if not self.devices:
print("⚠️ No devices to probe, fetching device list...")
if not self.fetch_devices():
print("❌ Cannot continue without device list")
return
print(f"\n🔄 Starting probe cycle at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Probing {len(self.devices)} devices...")
successful_probes = 0
failed_probes = 0
for device in self.devices:
if self.send_heartbeat(device):
successful_probes += 1
else:
failed_probes += 1
# Small delay between probes to avoid overwhelming the server
time.sleep(0.5)
success_rate = (successful_probes / len(self.devices)) * 100 if self.devices else 0
print(f"\n📊 Probe Cycle Summary:")
print(f" ✅ Successful: {successful_probes}")
print(f" ❌ Failed: {failed_probes}")
print(f" 📈 Success Rate: {success_rate:.1f}%")
print(f" 🎯 Target Failure Rate: {self.probe_failrate}%")
print(f" ⏰ Next cycle in {self.probe_interval} seconds")
def run_continuous(self):
"""Run continuous health check probes"""
print("🚀 Starting continuous health probe simulator...")
print(" Press Ctrl+C to stop")
print()
# Initial device fetch
if not self.fetch_devices():
print("❌ Failed to fetch initial device list, exiting")
return
try:
while True:
self.run_probe_cycle()
# Re-fetch devices every 10 cycles to catch new devices
if hasattr(self, 'cycle_count'):
self.cycle_count += 1
else:
self.cycle_count = 1
if self.cycle_count % 10 == 0:
print(f"\n🔄 Refreshing device list (cycle {self.cycle_count})...")
self.fetch_devices()
# Wait for next cycle
time.sleep(self.probe_interval)
except KeyboardInterrupt:
print(f"\n\n🛑 Health probe simulator stopped by user")
except Exception as e:
print(f"\n\n❌ Unexpected error in probe simulator: {e}")
def main():
print("=" * 60)
print("🏥 DRONE DETECTION SYSTEM - HEALTH PROBE SIMULATOR")
print("=" * 60)
print()
# Load environment variables
probe_failrate = os.getenv('PROBE_FAILRATE', '30')
probe_interval = os.getenv('PROBE_INTERVAL_SECONDS', '60')
api_url = os.getenv('API_BASE_URL', 'https://selfservice.cqers.com/drones/api')
print(f"📋 Configuration:")
print(f" PROBE_FAILRATE: {probe_failrate}%")
print(f" PROBE_INTERVAL_SECONDS: {probe_interval}s")
print(f" API_BASE_URL: {api_url}")
print()
simulator = DeviceProbeSimulator()
simulator.run_continuous()
if __name__ == "__main__":
main()

View File

@@ -158,23 +158,56 @@ class AlertService {
model: User,
as: 'user',
where: { is_active: true }
}]
}],
order: [
// Order by priority: critical, high, medium, low
['priority', 'DESC']
]
});
console.log(`📋 Found ${alertRules.length} active alert rules`);
// ENHANCED: Filter rules that match this detection and find the highest priority one
const matchingRules = [];
for (const rule of alertRules) {
if (await this.shouldTriggerAlert(rule, detection, threatAssessment)) {
await this.triggerAlert(rule, detection, threatAssessment);
matchingRules.push(rule);
console.log(`✅ Rule "${rule.name}" matches (priority: ${rule.priority})`);
} else {
console.log(`❌ Rule "${rule.name}" does not match conditions`);
}
}
if (matchingRules.length === 0) {
console.log('📭 No alert rules match this detection');
return;
}
// PRIORITY-BASED DEDUPLICATION: Only trigger the highest priority rule
const highestPriorityRule = matchingRules[0]; // Already sorted by priority DESC
console.log(`🎯 ALERT DEDUPLICATION: ${matchingRules.length} rules matched, triggering only highest priority:`);
console.log(` Selected Rule: "${highestPriorityRule.name}" (priority: ${highestPriorityRule.priority})`);
if (matchingRules.length > 1) {
console.log(` Skipped Rules:`);
matchingRules.slice(1).forEach(rule => {
console.log(` - "${rule.name}" (priority: ${rule.priority})`);
});
}
// Trigger only the highest priority alert
await this.triggerAlert(highestPriorityRule, detection, threatAssessment);
// Track active alert for potential clear notification
const alertKey = `${rule.id}-${detection.device_id}`;
const alertKey = `${highestPriorityRule.id}-${detection.device_id}`;
this.activeAlerts.set(alertKey, {
rule,
rule: highestPriorityRule,
detection,
threatAssessment,
alertTime: new Date()
});
}
}
} catch (error) {
console.error('Error processing alert:', error);
@@ -184,64 +217,84 @@ class AlertService {
async shouldTriggerAlert(rule, detection, threatAssessment) {
try {
// CRITICAL SECURITY: Orlan drones (type 1) ALWAYS trigger alerts regardless of any other conditions
if (detection.drone_type === 1) {
console.log(`🚨 ORLAN DRONE DETECTED - Force triggering alert for rule ${rule.name} (bypassing all filters)`);
console.log(`🔍 Evaluating rule "${rule.name}" for detection from device ${detection.device_id}`);
// PRIORITY 1: Device-specific filter (most important)
if (rule.device_ids && rule.device_ids.length > 0) {
if (!rule.device_ids.includes(detection.device_id)) {
console.log(`❌ Rule "${rule.name}": Device ${detection.device_id} not in allowed devices [${rule.device_ids.join(', ')}]`);
return false;
} else {
console.log(`✅ Rule "${rule.name}": Device ${detection.device_id} is in allowed devices`);
}
} else {
console.log(`✅ Rule "${rule.name}": No device filter (applies to all devices)`);
}
// PRIORITY 2: Critical threats and Orlan drones (security overrides)
const isOrlanDrone = detection.drone_type === 1;
const isCriticalThreat = threatAssessment.level === 'critical';
if (isOrlanDrone) {
console.log(`🚨 ORLAN DRONE DETECTED - Rule "${rule.name}" will trigger (security override)`);
return true;
}
// SECURITY ENHANCEMENT: Check threat level requirements
if (isCriticalThreat) {
console.log(`🚨 CRITICAL THREAT DETECTED - Rule "${rule.name}" will trigger (security override)`);
return true;
}
// PRIORITY 3: Threat level requirements
if (rule.min_threat_level) {
const threatLevels = { 'monitoring': 0, 'low': 1, 'medium': 2, 'high': 3, 'critical': 4 };
const requiredLevel = threatLevels[rule.min_threat_level] || 0;
const currentLevel = threatLevels[threatAssessment.level] || 0;
if (currentLevel < requiredLevel) {
console.log(`Alert rule ${rule.name}: Threat level ${threatAssessment.level} below minimum ${rule.min_threat_level}`);
console.log(`❌ Rule "${rule.name}": Threat level ${threatAssessment.level} below minimum ${rule.min_threat_level}`);
return false;
} else {
console.log(`✅ Rule "${rule.name}": Threat level ${threatAssessment.level} meets minimum ${rule.min_threat_level}`);
}
}
// SECURITY ENHANCEMENT: For government/sensitive sites, always alert on critical threats
if (threatAssessment.level === 'critical') {
console.log(`🚨 CRITICAL THREAT DETECTED - Force triggering alert for rule ${rule.name}`);
return true;
}
// Check device filter
if (rule.device_ids && rule.device_ids.length > 0 &&
!rule.device_ids.includes(detection.device_id)) {
return false;
}
// Check drone type filter
// PRIORITY 4: Drone type filter
if (rule.drone_types && rule.drone_types.length > 0 &&
!rule.drone_types.includes(detection.drone_type)) {
console.log(`❌ Rule "${rule.name}": Drone type ${detection.drone_type} not in allowed types [${rule.drone_types.join(', ')}]`);
return false;
}
// Check RSSI thresholds (enhanced for security)
// PRIORITY 5: RSSI thresholds (distance-based)
if (rule.min_rssi && detection.rssi < rule.min_rssi) {
console.log(`❌ Rule "${rule.name}": RSSI ${detection.rssi} below minimum ${rule.min_rssi}`);
return false;
}
// SECURITY ENHANCEMENT: Check estimated distance
if (rule.max_rssi && detection.rssi > rule.max_rssi) {
console.log(`❌ Rule "${rule.name}": RSSI ${detection.rssi} above maximum ${rule.max_rssi}`);
return false;
}
// PRIORITY 6: Distance requirements
if (rule.max_distance && threatAssessment.estimatedDistance > rule.max_distance) {
console.log(`Alert rule ${rule.name}: Distance ${threatAssessment.estimatedDistance}m exceeds maximum ${rule.max_distance}m`);
console.log(`❌ Rule "${rule.name}": Distance ${threatAssessment.estimatedDistance}m exceeds maximum ${rule.max_distance}m`);
return false;
}
// Check frequency ranges
// PRIORITY 7: Frequency ranges
if (rule.frequency_ranges && rule.frequency_ranges.length > 0) {
const inRange = rule.frequency_ranges.some(range =>
detection.freq >= range.min && detection.freq <= range.max
);
if (!inRange) {
console.log(`❌ Rule "${rule.name}": Frequency ${detection.freq}MHz not in allowed ranges`);
return false;
}
}
// Check time window and minimum detections
// PRIORITY 8: Time window and minimum detections
if (rule.min_detections > 1) {
const timeWindowStart = new Date(Date.now() - rule.time_window * 1000);
const recentDetections = await DroneDetection.count({
@@ -255,11 +308,12 @@ class AlertService {
});
if (recentDetections < rule.min_detections) {
console.log(`❌ Rule "${rule.name}": Only ${recentDetections} detections, need ${rule.min_detections}`);
return false;
}
}
// Check cooldown period
// PRIORITY 9: Cooldown period check
if (rule.cooldown_period > 0) {
const cooldownStart = new Date(Date.now() - rule.cooldown_period * 1000);
const recentAlert = await AlertLog.findOne({
@@ -281,11 +335,12 @@ class AlertService {
});
if (recentAlert) {
console.log(`❌ Rule "${rule.name}": Still in cooldown period`);
return false;
}
}
// Check active hours and days
// PRIORITY 10: Active hours and days
if (rule.active_hours || rule.active_days) {
const now = new Date();
const currentHour = now.getHours();
@@ -294,6 +349,7 @@ class AlertService {
const currentDay = now.getDay() || 7; // Convert Sunday from 0 to 7
if (rule.active_days && !rule.active_days.includes(currentDay)) {
console.log(`❌ Rule "${rule.name}": Not active on day ${currentDay}`);
return false;
}
@@ -305,11 +361,13 @@ class AlertService {
if (startTime <= endTime) {
// Same day range
if (currentTime < startTime || currentTime > endTime) {
console.log(`❌ Rule "${rule.name}": Outside active hours`);
return false;
}
} else {
// Overnight range
if (currentTime < startTime && currentTime > endTime) {
console.log(`❌ Rule "${rule.name}": Outside active hours (overnight)`);
return false;
}
}
@@ -317,10 +375,11 @@ class AlertService {
}
}
console.log(`✅ Rule "${rule.name}": All conditions met - WILL TRIGGER`);
return true;
} catch (error) {
console.error('Error checking alert conditions:', error);
console.error(`Error evaluating alert rule ${rule.name}:`, error);
return false;
}
}

46
start-healthprobe.bat Normal file
View File

@@ -0,0 +1,46 @@
@echo off
REM Health Probe Simulator Startup Script (Windows)
REM This script starts the health probe simulator that continuously sends heartbeats
echo 🏥 Starting Health Probe Simulator...
echo ==================================
REM Set defaults if not provided
if not defined PROBE_FAILRATE set PROBE_FAILRATE=30
if not defined PROBE_INTERVAL_SECONDS set PROBE_INTERVAL_SECONDS=60
if not defined API_BASE_URL set API_BASE_URL=https://selfservice.cqers.com/drones/api
echo 🔧 Configuration:
echo Failure Rate: %PROBE_FAILRATE%%%
echo Probe Interval: %PROBE_INTERVAL_SECONDS% seconds
echo API URL: %API_BASE_URL%
echo.
REM Check if running with Docker
if "%1"=="docker" (
echo 🐳 Starting with Docker Compose...
docker-compose --profile healthprobe up healthprobe
) else (
echo 💻 Running locally...
REM Check if Python is available
python --version >nul 2>&1
if errorlevel 1 (
echo ❌ Python is required but not installed
pause
exit /b 1
)
REM Check if requests module is available
python -c "import requests" >nul 2>&1
if errorlevel 1 (
echo 📦 Installing requests module...
pip install requests
)
REM Run the health probe simulator
echo 🚀 Starting health probe simulator...
python health_probe_simulator.py
)
pause

48
start-healthprobe.sh Normal file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
# Health Probe Simulator Startup Script
# This script starts the health probe simulator that continuously sends heartbeats
echo "🏥 Starting Health Probe Simulator..."
echo "=================================="
# Load environment variables
if [ -f .env ]; then
echo "📋 Loading environment from .env file..."
export $(cat .env | grep -v '^#' | xargs)
fi
# Set defaults if not provided
export PROBE_FAILRATE=${PROBE_FAILRATE:-30}
export PROBE_INTERVAL_SECONDS=${PROBE_INTERVAL_SECONDS:-60}
export API_BASE_URL=${API_BASE_URL:-https://selfservice.cqers.com/drones/api}
echo "🔧 Configuration:"
echo " Failure Rate: ${PROBE_FAILRATE}%"
echo " Probe Interval: ${PROBE_INTERVAL_SECONDS} seconds"
echo " API URL: ${API_BASE_URL}"
echo ""
# Check if running in Docker
if [ -n "$DOCKER_CONTAINER" ]; then
echo "🐳 Running in Docker container..."
# Run with Docker Compose profile
docker-compose --profile healthprobe up healthprobe
else
echo "💻 Running locally..."
# Check if Python is available
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 is required but not installed"
exit 1
fi
# Check if requests module is available
if ! python3 -c "import requests" &> /dev/null; then
echo "📦 Installing requests module..."
pip3 install requests
fi
# Run the health probe simulator
echo "🚀 Starting health probe simulator..."
python3 health_probe_simulator.py
fi