Fix jwt-token
This commit is contained in:
@@ -17,28 +17,9 @@ echo "Database is ready!"
|
|||||||
|
|
||||||
# Check if this is a fresh database by looking for the devices table
|
# Check if this is a fresh database by looking for the devices table
|
||||||
echo "Checking database state..."
|
echo "Checking database state..."
|
||||||
if su-exec nodejs node -e "
|
# Always run database setup (includes migrations + seeding if needed)
|
||||||
const { Sequelize } = require('sequelize');
|
echo "Running database setup and migrations..."
|
||||||
require('dotenv').config();
|
|
||||||
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, {
|
|
||||||
host: process.env.DB_HOST, port: process.env.DB_PORT, dialect: 'postgres', logging: false
|
|
||||||
});
|
|
||||||
sequelize.authenticate().then(() => {
|
|
||||||
return sequelize.getQueryInterface().describeTable('devices');
|
|
||||||
}).then(() => {
|
|
||||||
console.log('TABLES_EXIST');
|
|
||||||
process.exit(0);
|
|
||||||
}).catch(() => {
|
|
||||||
console.log('FRESH_DATABASE');
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
" | grep -q "FRESH_DATABASE"; then
|
|
||||||
echo "Fresh database detected. Running initial setup..."
|
|
||||||
su-exec nodejs npm run db:setup
|
su-exec nodejs npm run db:setup
|
||||||
else
|
|
||||||
echo "Existing database detected. Running migrations..."
|
|
||||||
su-exec nodejs npm run db:migrate
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if setup/migrations were successful
|
# Check if setup/migrations were successful
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
|
|||||||
614
server/migrations/20250820000001-create-initial-tables.js
Normal file
614
server/migrations/20250820000001-create-initial-tables.js
Normal 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');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
const { Sequelize } = require('sequelize');
|
const { Sequelize } = require('sequelize');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
|
const runMigrations = require('./migrate');
|
||||||
|
|
||||||
// Import models from the main models index
|
// Import models from the main models index
|
||||||
const {
|
const {
|
||||||
@@ -25,10 +26,19 @@ const setupDatabase = async () => {
|
|||||||
// Models are already initialized through the imports
|
// Models are already initialized through the imports
|
||||||
console.log('<27> Models loaded and ready...');
|
console.log('<27> Models loaded and ready...');
|
||||||
|
|
||||||
// Sync database (create tables)
|
// Run migrations first to create proper schema
|
||||||
console.log('🏗️ Creating database tables...');
|
console.log('🏗️ Running database migrations...');
|
||||||
await sequelize.sync({ force: true }); // WARNING: This will drop existing tables
|
await runMigrations();
|
||||||
console.log('✅ Database tables created successfully.\n');
|
console.log('✅ Database migrations completed successfully.\n');
|
||||||
|
|
||||||
|
// Check if sample data already exists
|
||||||
|
const existingTenants = await Tenant.count();
|
||||||
|
if (existingTenants > 0) {
|
||||||
|
console.log('📊 Sample data already exists, skipping data creation...\n');
|
||||||
|
console.log('🎉 Database setup completed successfully!\n');
|
||||||
|
await sequelize.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create sample data
|
// Create sample data
|
||||||
console.log('📊 Creating sample data...\n');
|
console.log('📊 Creating sample data...\n');
|
||||||
@@ -284,8 +294,8 @@ const setupDatabase = async () => {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
await sequelize.query(`
|
await sequelize.query(`
|
||||||
CREATE INDEX IF NOT EXISTS idx_alert_logs_user_created
|
CREATE INDEX IF NOT EXISTS idx_alert_logs_rule_created
|
||||||
ON "alert_logs" (user_id, "created_at");
|
ON "alert_logs" (alert_rule_id, "created_at");
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log('✅ Database indexes created\n');
|
console.log('✅ Database indexes created\n');
|
||||||
|
|||||||
Reference in New Issue
Block a user