Fix jwt-token

This commit is contained in:
2025-08-18 05:42:33 +02:00
parent af2a1078ac
commit 8a9a5a7e3b
4 changed files with 149 additions and 9 deletions

View File

@@ -116,6 +116,17 @@ async function startServer() {
console.log('📱 SMS Alerts: ⚠️ Disabled (no Twilio credentials)');
}
// Start periodic clear alert checking (every 2 minutes)
setInterval(async () => {
try {
await alertService.checkClearedAlerts();
} catch (error) {
console.error('Error checking cleared alerts:', error);
}
}, 2 * 60 * 1000); // Check every 2 minutes
console.log('🔄 Clear alert monitoring: ✅ Started');
console.log(`📊 Health check: http://localhost:${PORT}/health`);
console.log(`🌐 API endpoint: http://localhost:${PORT}/api`);
console.log('================================================\n');

View File

@@ -74,6 +74,11 @@ module.exports = (sequelize) => {
defaultValue: ['sms'],
comment: 'Array of alert channels: sms, email, webhook'
},
sms_phone_number: {
type: DataTypes.STRING,
allowNull: true,
comment: 'Phone number for SMS alerts'
},
webhook_url: {
type: DataTypes.STRING,
allowNull: true,

View File

@@ -1,5 +1,5 @@
const bcrypt = require('bcryptjs');
const { User, Device } = require('./models');
const bcrypt = require('bcrypt');
const { User, Device, AlertRule } = require('./models');
async function seedDatabase() {
try {
@@ -74,6 +74,65 @@ async function seedDatabase() {
console.log('✅ Devices already exist');
}
// Get admin user for alert rules
const adminUser = await User.findOne({ where: { username: 'admin' } });
// Create location-specific alert rules
const alertRuleCount = await AlertRule.count();
if (alertRuleCount === 0) {
await AlertRule.bulkCreate([
{
user_id: adminUser.id,
name: 'Arlanda Airport Security Alert',
description: 'High-priority drone detection alert for Arlanda Airport',
is_active: true,
device_ids: [1], // Arlanda detector only
min_rssi: -85, // Alert on any detection
priority: 'critical',
alert_channels: ['sms'],
sms_phone_number: '0736419592',
cooldown_period: 300, // 5 minutes between alerts
min_detections: 1,
time_window: 60 // 1 minute window
},
{
user_id: adminUser.id,
name: 'Muskö Naval Base Security Alert',
description: 'High-priority drone detection alert for Muskö Naval Base',
is_active: true,
device_ids: [2], // Muskö detector only
min_rssi: -85, // Alert on any detection
priority: 'critical',
alert_channels: ['sms'],
sms_phone_number: '0739999999',
cooldown_period: 300, // 5 minutes between alerts
min_detections: 1,
time_window: 60 // 1 minute window
},
{
user_id: adminUser.id,
name: 'Royal Castle Security Alert',
description: 'High-priority drone detection alert for Royal Castle',
is_active: true,
device_ids: [3], // Royal Castle detector only
min_rssi: -85, // Alert on any detection
priority: 'critical',
alert_channels: ['sms'],
sms_phone_number: '0739999999',
cooldown_period: 300, // 5 minutes between alerts
min_detections: 1,
time_window: 60 // 1 minute window
}
]);
console.log('✅ Location-specific alert rules created:');
console.log(' - Arlanda Airport → 0736419592');
console.log(' - Muskö Naval Base → 073999999');
console.log(' - Royal Castle → 073999999');
} else {
console.log('✅ Alert rules already exist');
}
console.log('🌱 Database seeding completed');
} catch (error) {
console.error('❌ Database seeding failed:', error);

View File

@@ -7,6 +7,7 @@ class AlertService {
this.twilioClient = null;
this.twilioPhone = null;
this.twilioEnabled = false;
this.activeAlerts = new Map(); // Track active alerts for clear notifications
this.initializeTwilio();
}
@@ -156,6 +157,15 @@ class AlertService {
for (const rule of alertRules) {
if (await this.shouldTriggerAlert(rule, detection, threatAssessment)) {
await this.triggerAlert(rule, detection, threatAssessment);
// Track active alert for potential clear notification
const alertKey = `${rule.id}-${detection.device_id}`;
this.activeAlerts.set(alertKey, {
rule,
detection,
threatAssessment,
alertTime: new Date()
});
}
}
@@ -313,7 +323,7 @@ class AlertService {
// SECURITY ENHANCEMENT: For critical threats, send to all available channels
const channels = threatAssessment.level === 'critical'
? ['sms', 'email', 'webhook'] // Force all channels for critical threats
: rule.actions.channels || ['sms'];
: rule.alert_channels || ['sms'];
// Send alerts through configured channels
for (const channel of channels) {
@@ -322,8 +332,8 @@ class AlertService {
try {
switch (channel) {
case 'sms':
if (rule.actions.sms && rule.actions.phone_number) {
alertLog = await this.sendSMSAlert(rule.actions.phone_number, message, rule, detection, threatAssessment);
if (rule.alert_channels.includes('sms') && rule.sms_phone_number) {
alertLog = await this.sendSMSAlert(rule.sms_phone_number, message, rule, detection, threatAssessment);
}
break;
@@ -375,7 +385,8 @@ class AlertService {
// Check if Twilio is enabled
if (!this.twilioEnabled || !this.twilioClient) {
console.log('📱 SMS alert skipped - Twilio not configured');
console.log(`📱 Would have sent to ${phoneNumber}: ${message}`);
console.log(`📱 Would have sent to ${phoneNumber}:`);
console.log(`📱 Message: ${message}`);
return await AlertLog.create({
alert_rule_id: rule.id,
@@ -392,7 +403,8 @@ class AlertService {
}
try {
console.log(`📱 Sending SMS alert to ${phoneNumber}`);
console.log(`📱 Sending SMS alert to ${phoneNumber} for rule: ${rule.name}`);
console.log(`📱 Message: ${message}`);
const twilioMessage = await this.twilioClient.messages.create({
body: message,
@@ -400,7 +412,7 @@ class AlertService {
to: phoneNumber
});
console.log(`✅ SMS sent successfully: ${twilioMessage.sid}`);
console.log(`✅ SMS sent successfully to ${phoneNumber}: ${twilioMessage.sid}`);
return await AlertLog.create({
alert_rule_id: rule.id,
@@ -414,7 +426,7 @@ class AlertService {
priority: rule.priority
});
} catch (error) {
console.error('❌ Failed to send SMS:', error.message);
console.error(`❌ Failed to send SMS to ${phoneNumber}:`, error.message);
return await AlertLog.create({
alert_rule_id: rule.id,
@@ -553,6 +565,59 @@ class AlertService {
return hours * 60 + minutes;
}
// Check for cleared alerts (call this periodically)
async checkClearedAlerts() {
try {
const now = new Date();
const clearThreshold = 5 * 60 * 1000; // 5 minutes without detection = cleared
for (const [alertKey, alertData] of this.activeAlerts.entries()) {
const timeSinceAlert = now - alertData.alertTime;
if (timeSinceAlert > clearThreshold) {
// Check if there are any recent detections from this device
const recentDetections = await DroneDetection.count({
where: {
device_id: alertData.detection.device_id,
device_timestamp: {
[Op.gte]: new Date(now - clearThreshold)
}
}
});
if (recentDetections === 0) {
await this.sendClearAlert(alertData);
this.activeAlerts.delete(alertKey);
}
}
}
} catch (error) {
console.error('Error checking cleared alerts:', error);
}
}
async sendClearAlert(alertData) {
const { rule, detection } = alertData;
if (rule.alert_channels.includes('sms') && rule.sms_phone_number) {
const device = await Device.findByPk(detection.device_id);
const clearMessage = this.generateClearMessage(device, rule);
console.log(`🟢 ALERT CLEARED - Sending clear notification for ${device.name} to ${rule.sms_phone_number}`);
await this.sendSMSAlert(rule.sms_phone_number, clearMessage, rule, detection, null);
}
}
generateClearMessage(device, rule) {
return `🟢 ALL CLEAR 🟢\n\n` +
`📍 LOCATION: ${device.location_description || device.name}\n` +
`🔧 DEVICE: ${device.name}\n` +
`⏰ TIME: ${new Date().toLocaleString('sv-SE')}\n\n` +
`✅ No drone activity detected for 5+ minutes.\n` +
`🛡️ Area is secure.`;
}
}
// Export the class directly (not a singleton instance)