Fix jwt-token
This commit is contained in:
@@ -37,6 +37,51 @@ router.post('/devices/:id/heartbeat', async (req, res) => {
|
||||
const { Device, Heartbeat } = models;
|
||||
const deviceId = parseInt(req.params.id);
|
||||
|
||||
// Validate heartbeat payload
|
||||
const { type, key, status, cpu_usage, memory_usage, disk_usage, uptime, firmware_version, timestamp } = req.body;
|
||||
|
||||
// Check for unexpected fields
|
||||
const allowedFields = ['type', 'key', 'status', 'cpu_usage', 'memory_usage', 'disk_usage', 'uptime', 'firmware_version', 'timestamp'];
|
||||
const receivedFields = Object.keys(req.body);
|
||||
const unexpectedFields = receivedFields.filter(field => !allowedFields.includes(field));
|
||||
|
||||
if (unexpectedFields.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Unexpected fields: ${unexpectedFields.join(', ')}`
|
||||
});
|
||||
}
|
||||
|
||||
// Validate status if provided
|
||||
if (status && !['online', 'offline', 'error', 'maintenance'].includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid status. Must be one of: online, offline, error, maintenance'
|
||||
});
|
||||
}
|
||||
|
||||
// Validate percentage fields
|
||||
const percentageFields = ['cpu_usage', 'memory_usage', 'disk_usage'];
|
||||
for (const field of percentageFields) {
|
||||
if (req.body[field] !== undefined) {
|
||||
const value = parseFloat(req.body[field]);
|
||||
if (isNaN(value) || value < 0 || value > 100) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `${field} must be a number between 0 and 100`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate timestamp if provided
|
||||
if (timestamp && isNaN(Date.parse(timestamp))) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid timestamp format'
|
||||
});
|
||||
}
|
||||
|
||||
// Find the device
|
||||
const device = await Device.findByPk(deviceId);
|
||||
if (!device) {
|
||||
@@ -50,12 +95,13 @@ router.post('/devices/:id/heartbeat', async (req, res) => {
|
||||
if (!device.is_approved) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Device not approved for heartbeat reporting'
|
||||
message: 'Device not approved for heartbeat reporting',
|
||||
approval_required: true
|
||||
});
|
||||
}
|
||||
|
||||
// Extract heartbeat data - handle both simple device heartbeats and detailed health reports
|
||||
const { type, key, status, cpu_usage, memory_usage, disk_usage, uptime, firmware_version } = req.body;
|
||||
// Variables already destructured above: type, key, status, cpu_usage, memory_usage, disk_usage, uptime, firmware_version
|
||||
|
||||
// For simple device heartbeats: {type:"heartbeat", key:"unique device ID"}
|
||||
// For detailed health reports: {status:"online", cpu_usage:25.5, memory_usage:60.2, etc.}
|
||||
@@ -486,6 +532,16 @@ router.get('/metrics', async (req, res) => {
|
||||
const models = getModels();
|
||||
const { sequelize } = models;
|
||||
|
||||
// Get user tenant ID with proper fallback
|
||||
const tenantId = req.user?.tenant_id || req.tenantId || req.tenant?.id;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
status: 'error',
|
||||
message: 'No tenant context available'
|
||||
});
|
||||
}
|
||||
|
||||
// Get system metrics
|
||||
const memUsage = process.memoryUsage();
|
||||
const startTime = Date.now();
|
||||
@@ -501,7 +557,7 @@ router.get('/metrics', async (req, res) => {
|
||||
const [deviceCount] = await sequelize.query(
|
||||
'SELECT COUNT(*) as count FROM "Devices" WHERE tenant_id = :tenantId',
|
||||
{
|
||||
replacements: { tenantId: req.user.tenant_id },
|
||||
replacements: { tenantId: tenantId },
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
@@ -509,7 +565,7 @@ router.get('/metrics', async (req, res) => {
|
||||
const [detectionCount] = await sequelize.query(
|
||||
'SELECT COUNT(*) as count FROM "DroneDetections" WHERE tenant_id = :tenantId',
|
||||
{
|
||||
replacements: { tenantId: req.user.tenant_id },
|
||||
replacements: { tenantId: tenantId },
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
}
|
||||
);
|
||||
|
||||
@@ -392,7 +392,7 @@ describe('Models', () => {
|
||||
});
|
||||
|
||||
expect(detectionWithDevice.device).to.exist;
|
||||
expect(detectionWithDevice.device.id).to.equal(device.id);
|
||||
expect(detectionWithDevice.device.id).to.equal(String(device.id));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -537,7 +537,7 @@ describe('Models', () => {
|
||||
});
|
||||
|
||||
expect(logWithDevice.device).to.exist;
|
||||
expect(logWithDevice.device.id).to.equal(device.id);
|
||||
expect(logWithDevice.device.id).to.equal(String(device.id));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('Detections Routes', () => {
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.data.detections).to.have.length(1);
|
||||
expect(response.body.data.detections[0].device_id).to.equal(device1.id);
|
||||
expect(response.body.data.detections[0].device_id).to.equal(String(device1.id));
|
||||
});
|
||||
|
||||
it('should support filtering by drone type', async () => {
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('Device Routes', () => {
|
||||
expect(response.body.data).to.have.length(2);
|
||||
|
||||
const deviceIds = response.body.data.map(d => d.id);
|
||||
expect(deviceIds).to.include.members([123, 124]);
|
||||
expect(deviceIds).to.include.members(['123', '124']);
|
||||
});
|
||||
|
||||
it('should only return devices for user tenant', async () => {
|
||||
@@ -85,7 +85,7 @@ describe('Device Routes', () => {
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.data).to.have.length(1);
|
||||
expect(response.body.data[0].id).to.equal(111);
|
||||
expect(response.body.data[0].id).to.equal('111');
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
@@ -155,7 +155,7 @@ describe('Device Routes', () => {
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
expect(response.body.data.id).to.equal(12345);
|
||||
expect(response.body.data.id).to.equal('12345');
|
||||
expect(response.body.data.name).to.equal('Specific Device');
|
||||
expect(response.body.data.location_description).to.equal('Test Location');
|
||||
});
|
||||
@@ -570,18 +570,20 @@ describe('Device Routes', () => {
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.get('/devices')
|
||||
.get('/devices?include_stats=true')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const devices = response.body.data;
|
||||
|
||||
const online = devices.find(d => d.id === onlineDevice.id);
|
||||
const offline = devices.find(d => d.id === offlineDevice.id);
|
||||
const online = devices.find(d => d.id === String(onlineDevice.id));
|
||||
const offline = devices.find(d => d.id === String(offlineDevice.id));
|
||||
|
||||
// These assertions depend on your business logic for determining online status
|
||||
expect(online).to.exist;
|
||||
expect(online.stats).to.exist;
|
||||
expect(offline).to.exist;
|
||||
expect(offline.stats).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -244,7 +244,7 @@ describe('Healthcheck Routes', () => {
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.devices).to.be.an('array');
|
||||
|
||||
const deviceStatus = response.body.devices.find(d => d.id === device.id);
|
||||
const deviceStatus = response.body.devices.find(d => d.id === String(device.id));
|
||||
expect(deviceStatus).to.exist;
|
||||
expect(deviceStatus.status).to.equal('online');
|
||||
expect(deviceStatus.metrics).to.exist;
|
||||
@@ -288,8 +288,8 @@ describe('Healthcheck Routes', () => {
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const deviceStatus = response.body.devices.find(d => d.id === device.id);
|
||||
expect(deviceStatus.uptime_hours).to.be.a('number');
|
||||
const deviceStatus = response.body.devices.find(d => d.id === String(device.id));
|
||||
expect(deviceStatus.uptime).to.be.a('number');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -504,9 +504,9 @@ describe('Healthcheck Routes', () => {
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
// Generate some database activity
|
||||
await createTestDevice({ tenant_id: tenant.id });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
await models.DroneDetection.create({
|
||||
device_id: 123,
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
@@ -566,7 +566,7 @@ describe('Healthcheck Routes', () => {
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const deviceStatus = response.body.devices.find(d => d.id === device.id);
|
||||
const deviceStatus = response.body.devices.find(d => d.id === String(device.id));
|
||||
expect(deviceStatus.status).to.be.oneOf(['offline', 'stale', 'unknown']);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user