Fix jwt-token
This commit is contained in:
@@ -292,9 +292,14 @@ router.get('/devices', async (req, res) => {
|
|||||||
const deviceDetails = [];
|
const deviceDetails = [];
|
||||||
|
|
||||||
for (const device of devices) {
|
for (const device of devices) {
|
||||||
const lastHeartbeat = device.heartbeats && device.heartbeats[0];
|
// Get latest heartbeat metrics for this device
|
||||||
const isOnline = lastHeartbeat &&
|
const latestHeartbeat = await Heartbeat.findOne({
|
||||||
(Date.now() - new Date(lastHeartbeat.timestamp).getTime()) < 300000; // 5 minutes
|
where: { device_id: device.id },
|
||||||
|
order: [['timestamp', 'DESC']]
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOnline = latestHeartbeat &&
|
||||||
|
(Date.now() - new Date(latestHeartbeat.timestamp).getTime()) < 300000; // 5 minutes
|
||||||
|
|
||||||
if (isOnline) {
|
if (isOnline) {
|
||||||
onlineDevices++;
|
onlineDevices++;
|
||||||
@@ -302,12 +307,29 @@ router.get('/devices', async (req, res) => {
|
|||||||
offlineDevices++;
|
offlineDevices++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate uptime (time since first heartbeat)
|
||||||
|
const firstHeartbeat = await Heartbeat.findOne({
|
||||||
|
where: { device_id: device.id },
|
||||||
|
order: [['timestamp', 'ASC']]
|
||||||
|
});
|
||||||
|
|
||||||
|
let uptime = null;
|
||||||
|
if (firstHeartbeat && latestHeartbeat) {
|
||||||
|
uptime = Math.floor((new Date(latestHeartbeat.timestamp).getTime() - new Date(firstHeartbeat.timestamp).getTime()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
deviceDetails.push({
|
deviceDetails.push({
|
||||||
id: device.id,
|
id: device.id,
|
||||||
name: device.name,
|
name: device.name,
|
||||||
status: isOnline ? 'online' : 'offline',
|
status: isOnline ? 'online' : 'offline',
|
||||||
lastSeen: lastHeartbeat ? lastHeartbeat.timestamp : null,
|
lastSeen: latestHeartbeat ? latestHeartbeat.timestamp : null,
|
||||||
uptime: lastHeartbeat ? Math.floor(Date.now() / 1000) - Math.floor(new Date(lastHeartbeat.timestamp).getTime() / 1000) : null
|
uptime: uptime,
|
||||||
|
metrics: latestHeartbeat ? {
|
||||||
|
cpu_usage: latestHeartbeat.cpu_usage,
|
||||||
|
memory_usage: latestHeartbeat.memory_usage,
|
||||||
|
disk_usage: latestHeartbeat.disk_usage,
|
||||||
|
uptime: latestHeartbeat.uptime
|
||||||
|
} : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,17 +372,58 @@ router.post('/devices/:id/heartbeat', async (req, res) => {
|
|||||||
if (!device.is_approved) {
|
if (!device.is_approved) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: 'Device not approved for heartbeat reporting'
|
message: 'Device not approved for heartbeat reporting',
|
||||||
|
approval_required: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate heartbeat data format
|
// Validate heartbeat data format
|
||||||
const { status, cpu_usage, memory_usage, disk_usage } = req.body;
|
const { timestamp, status, cpu_usage, memory_usage, disk_usage } = req.body;
|
||||||
|
|
||||||
if (!status || typeof status !== 'string') {
|
if (!status || typeof status !== 'string') {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
message: 'Invalid heartbeat data format - status is required'
|
message: 'Invalid heartbeat data format - status is required and must be a string'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate status values
|
||||||
|
const validStatuses = ['online', 'offline', 'error', 'maintenance'];
|
||||||
|
if (!validStatuses.includes(status)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
status: 'error',
|
||||||
|
message: `Invalid status value. Must be one of: ${validStatuses.join(', ')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate timestamp if provided
|
||||||
|
if (timestamp && isNaN(new Date(timestamp).getTime())) {
|
||||||
|
return res.status(400).json({
|
||||||
|
status: 'error',
|
||||||
|
message: 'Invalid timestamp format'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate percentage fields
|
||||||
|
const percentageFields = { cpu_usage, memory_usage, disk_usage };
|
||||||
|
for (const [field, value] of Object.entries(percentageFields)) {
|
||||||
|
if (value !== undefined && value !== null && (typeof value !== 'number' || value < 0 || value > 100)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
status: 'error',
|
||||||
|
message: `Invalid ${field} value. Must be a number between 0 and 100`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unexpected fields
|
||||||
|
const allowedFields = ['timestamp', 'status', 'cpu_usage', 'memory_usage', 'disk_usage', 'uptime'];
|
||||||
|
const receivedFields = Object.keys(req.body);
|
||||||
|
const invalidFields = receivedFields.filter(field => !allowedFields.includes(field));
|
||||||
|
|
||||||
|
if (invalidFields.length > 0) {
|
||||||
|
return res.status(400).json({
|
||||||
|
status: 'error',
|
||||||
|
message: `Invalid fields in heartbeat data: ${invalidFields.join(', ')}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,18 +494,51 @@ router.get('/metrics', async (req, res) => {
|
|||||||
await sequelize.authenticate();
|
await sequelize.authenticate();
|
||||||
const dbResponseTime = Date.now() - startTime;
|
const dbResponseTime = Date.now() - startTime;
|
||||||
|
|
||||||
|
// Get CPU usage
|
||||||
|
const cpuUsage = process.cpuUsage();
|
||||||
|
|
||||||
|
// Get database statistics
|
||||||
|
const [deviceCount] = await sequelize.query(
|
||||||
|
'SELECT COUNT(*) as count FROM "Devices" WHERE tenant_id = :tenantId',
|
||||||
|
{
|
||||||
|
replacements: { tenantId: req.user.tenant_id },
|
||||||
|
type: sequelize.QueryTypes.SELECT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const [detectionCount] = await sequelize.query(
|
||||||
|
'SELECT COUNT(*) as count FROM "DroneDetections" WHERE tenant_id = :tenantId',
|
||||||
|
{
|
||||||
|
replacements: { tenantId: req.user.tenant_id },
|
||||||
|
type: sequelize.QueryTypes.SELECT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
metrics: {
|
system: {
|
||||||
memory: {
|
memory: {
|
||||||
heapUsed: Math.round((memUsage.heapUsed / 1024 / 1024) * 100) / 100,
|
heapUsed: Math.round((memUsage.heapUsed / 1024 / 1024) * 100) / 100,
|
||||||
heapTotal: Math.round((memUsage.heapTotal / 1024 / 1024) * 100) / 100,
|
heapTotal: Math.round((memUsage.heapTotal / 1024 / 1024) * 100) / 100,
|
||||||
external: Math.round((memUsage.external / 1024 / 1024) * 100) / 100
|
external: Math.round((memUsage.external / 1024 / 1024) * 100) / 100
|
||||||
},
|
},
|
||||||
database: {
|
cpu: {
|
||||||
responseTime: dbResponseTime
|
user: cpuUsage.user,
|
||||||
|
system: cpuUsage.system
|
||||||
},
|
},
|
||||||
uptime: process.uptime()
|
uptime: process.uptime()
|
||||||
|
},
|
||||||
|
database: {
|
||||||
|
responseTime: dbResponseTime,
|
||||||
|
connection_pool: {
|
||||||
|
active: sequelize.connectionManager.pool._count || 0,
|
||||||
|
idle: sequelize.connectionManager.pool._idle?.length || 0,
|
||||||
|
total: sequelize.connectionManager.pool.options.max || 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
statistics: {
|
||||||
|
total_devices: parseInt(deviceCount.count),
|
||||||
|
total_detections: parseInt(detectionCount.count)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user