Fix jwt-token

This commit is contained in:
2025-09-21 09:03:09 +02:00
parent a1cf28f76a
commit e52d6a8384
3 changed files with 633 additions and 28 deletions

View File

@@ -0,0 +1,614 @@
/**
* Initial Migration: Create all base tables
* This migration creates the core database structure
*/
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
// Create tenants table first (referenced by other tables)
await queryInterface.createTable('tenants', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
allowNull: false
},
name: {
type: Sequelize.STRING,
allowNull: false,
comment: 'Organization or tenant name'
},
slug: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
comment: 'URL-friendly identifier'
},
domain: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Domain for SSO integration'
},
subdomain: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Subdomain for multi-tenant routing'
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true,
comment: 'Whether tenant is active'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
}
});
// Create users table
await queryInterface.createTable('users', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
allowNull: false
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
email: {
type: Sequelize.STRING,
allowNull: true,
validate: {
isEmail: true
}
},
password_hash: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.ENUM('admin', 'operator', 'viewer'),
defaultValue: 'viewer',
allowNull: false
},
tenant_id: {
type: Sequelize.UUID,
allowNull: true,
references: {
model: 'tenants',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
}
});
// Create devices table
await queryInterface.createTable('devices', {
id: {
type: Sequelize.STRING(255),
primaryKey: true,
allowNull: false,
comment: 'Unique device identifier'
},
name: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Human-readable device name'
},
geo_lat: {
type: Sequelize.DECIMAL(10, 8),
allowNull: true,
comment: 'Device latitude coordinate'
},
geo_lon: {
type: Sequelize.DECIMAL(11, 8),
allowNull: true,
comment: 'Device longitude coordinate'
},
location_description: {
type: Sequelize.TEXT,
allowNull: true,
comment: 'Human-readable location description'
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true,
comment: 'Whether the device is currently active'
},
last_heartbeat: {
type: Sequelize.DATE,
allowNull: true,
comment: 'Timestamp of last heartbeat received'
},
heartbeat_interval: {
type: Sequelize.INTEGER,
defaultValue: 300,
comment: 'Expected heartbeat interval in seconds'
},
firmware_version: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Device firmware version'
},
installation_date: {
type: Sequelize.DATE,
allowNull: true,
comment: 'When the device was installed'
},
notes: {
type: Sequelize.TEXT,
allowNull: true,
comment: 'Additional notes about the device'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
allowNull: false
}
});
// Create heartbeats table
await queryInterface.createTable('heartbeats', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
device_id: {
type: Sequelize.STRING(255),
allowNull: false,
references: {
model: 'devices',
key: 'id'
},
comment: 'ID of the device sending heartbeat'
},
device_key: {
type: Sequelize.STRING,
allowNull: true,
defaultValue: 'test-device-key',
comment: 'Unique key of the sensor from heartbeat message'
},
status: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Device status (online, offline, error, etc.)'
},
timestamp: {
type: Sequelize.DATE,
allowNull: true,
comment: 'Timestamp from device'
},
uptime: {
type: Sequelize.BIGINT,
allowNull: true,
comment: 'Device uptime in seconds'
},
memory_usage: {
type: Sequelize.FLOAT,
allowNull: true,
comment: 'Memory usage percentage'
},
cpu_usage: {
type: Sequelize.FLOAT,
allowNull: true,
comment: 'CPU usage percentage'
},
disk_usage: {
type: Sequelize.FLOAT,
allowNull: true,
comment: 'Disk usage percentage'
},
firmware_version: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Firmware version reported in heartbeat'
},
received_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
comment: 'When heartbeat was received by server'
},
raw_payload: {
type: Sequelize.JSON,
allowNull: true,
comment: 'Complete raw payload received from detector (for debugging)'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create drone_detections table
await queryInterface.createTable('drone_detections', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
device_id: {
type: Sequelize.STRING(255),
allowNull: false,
references: {
model: 'devices',
key: 'id'
},
comment: 'ID of the detecting device'
},
drone_id: {
type: Sequelize.BIGINT,
allowNull: false,
comment: 'ID of the detected drone'
},
drone_type: {
type: Sequelize.INTEGER,
allowNull: true,
comment: 'Type of drone detected'
},
rssi: {
type: Sequelize.INTEGER,
allowNull: true,
comment: 'Signal strength in dBm'
},
freq: {
type: Sequelize.INTEGER,
allowNull: true,
comment: 'Frequency detected'
},
geo_lat: {
type: Sequelize.DECIMAL(10, 8),
allowNull: true,
comment: 'Latitude where detection occurred'
},
geo_lon: {
type: Sequelize.DECIMAL(11, 8),
allowNull: true,
comment: 'Longitude where detection occurred'
},
device_timestamp: {
type: Sequelize.BIGINT,
allowNull: true,
comment: 'Unix timestamp from the device'
},
server_timestamp: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW,
comment: 'When the detection was received by server'
},
confidence_level: {
type: Sequelize.DECIMAL(3, 2),
allowNull: true,
comment: 'Confidence level of detection (0.00-1.00)'
},
signal_duration: {
type: Sequelize.INTEGER,
allowNull: true,
comment: 'Duration of signal in milliseconds'
},
processed: {
type: Sequelize.BOOLEAN,
defaultValue: false,
comment: 'Whether this detection has been processed for alerts'
},
threat_level: {
type: Sequelize.STRING,
allowNull: true,
comment: 'Assessed threat level based on RSSI and drone type'
},
estimated_distance: {
type: Sequelize.INTEGER,
allowNull: true,
comment: 'Estimated distance to drone in meters'
},
requires_action: {
type: Sequelize.BOOLEAN,
defaultValue: false,
comment: 'Whether this detection requires immediate security action'
},
raw_payload: {
type: Sequelize.JSON,
allowNull: true,
comment: 'Complete raw payload received from detector (for debugging)'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create alert_rules table
await queryInterface.createTable('alert_rules', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
user_id: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: 'users',
key: 'id'
}
},
name: {
type: Sequelize.STRING,
allowNull: false
},
description: {
type: Sequelize.TEXT,
allowNull: true
},
conditions: {
type: Sequelize.JSON,
allowNull: false
},
actions: {
type: Sequelize.JSON,
allowNull: false
},
cooldown_minutes: {
type: Sequelize.INTEGER,
defaultValue: 5
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create alert_logs table
await queryInterface.createTable('alert_logs', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
alert_rule_id: {
type: Sequelize.UUID,
allowNull: true,
references: {
model: 'alert_rules',
key: 'id'
}
},
detection_id: {
type: Sequelize.UUID,
allowNull: true,
references: {
model: 'drone_detections',
key: 'id'
}
},
device_id: {
type: Sequelize.STRING(255),
allowNull: true,
references: {
model: 'devices',
key: 'id'
}
},
alert_type: {
type: Sequelize.ENUM('sms', 'email', 'webhook', 'push'),
allowNull: true,
defaultValue: 'sms'
},
recipient: {
type: Sequelize.STRING,
allowNull: true
},
message: {
type: Sequelize.TEXT,
allowNull: false
},
status: {
type: Sequelize.ENUM('pending', 'sent', 'failed', 'delivered'),
defaultValue: 'pending'
},
sent_at: {
type: Sequelize.DATE,
allowNull: true
},
delivered_at: {
type: Sequelize.DATE,
allowNull: true
},
error_message: {
type: Sequelize.TEXT,
allowNull: true
},
external_id: {
type: Sequelize.STRING,
allowNull: true
},
cost: {
type: Sequelize.DECIMAL(10, 4),
allowNull: true
},
retry_count: {
type: Sequelize.INTEGER,
defaultValue: 0
},
priority: {
type: Sequelize.ENUM('low', 'normal', 'high', 'urgent'),
defaultValue: 'normal'
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create AuditLogs table
await queryInterface.createTable('audit_logs', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
tenant_id: {
type: Sequelize.UUID,
allowNull: true,
references: {
model: 'tenants',
key: 'id'
}
},
user_id: {
type: Sequelize.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id'
}
},
action: {
type: Sequelize.STRING,
allowNull: false
},
resource_type: {
type: Sequelize.STRING,
allowNull: true
},
resource_id: {
type: Sequelize.STRING,
allowNull: true
},
details: {
type: Sequelize.JSON,
allowNull: true
},
ip_address: {
type: Sequelize.INET,
allowNull: true
},
user_agent: {
type: Sequelize.TEXT,
allowNull: true
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create management_users table
await queryInterface.createTable('management_users', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password_hash: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.ENUM('super_admin', 'tenant_admin'),
defaultValue: 'tenant_admin',
allowNull: false
},
permissions: {
type: Sequelize.JSON,
allowNull: true,
defaultValue: []
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
last_login: {
type: Sequelize.DATE,
allowNull: true
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
// Create basic indexes
await queryInterface.addIndex('devices', ['geo_lat', 'geo_lon']);
await queryInterface.addIndex('devices', ['is_active']);
await queryInterface.addIndex('heartbeats', ['device_id']);
await queryInterface.addIndex('heartbeats', ['received_at']);
await queryInterface.addIndex('drone_detections', ['device_id']);
await queryInterface.addIndex('drone_detections', ['drone_id']);
await queryInterface.addIndex('drone_detections', ['server_timestamp']);
await queryInterface.addIndex('alert_rules', ['user_id']);
await queryInterface.addIndex('alert_logs', ['alert_rule_id']);
await queryInterface.addIndex('audit_logs', ['tenant_id']);
await queryInterface.addIndex('audit_logs', ['user_id']);
await queryInterface.addIndex('audit_logs', ['created_at']);
},
async down(queryInterface, Sequelize) {
// Drop tables in reverse order due to foreign key constraints
await queryInterface.dropTable('audit_logs');
await queryInterface.dropTable('management_users');
await queryInterface.dropTable('alert_logs');
await queryInterface.dropTable('alert_rules');
await queryInterface.dropTable('drone_detections');
await queryInterface.dropTable('heartbeats');
await queryInterface.dropTable('devices');
await queryInterface.dropTable('users');
await queryInterface.dropTable('tenants');
}
};