Fix jwt-token

This commit is contained in:
2025-08-28 08:24:20 +02:00
parent 9b31494d9c
commit 7a6a2edd56
4 changed files with 34 additions and 299 deletions

View File

@@ -51,6 +51,11 @@ const Devices = () => {
fetchDevices();
} catch (error) {
console.error('Error approving device:', error);
if (error.response?.status === 401 || error.response?.status === 403) {
alert('Your session has expired. Please log in again.');
return;
}
alert('Error approving device: ' + (error.response?.data?.message || error.message));
}
};
@@ -61,6 +66,11 @@ const Devices = () => {
fetchDevices();
} catch (error) {
console.error('Error rejecting device:', error);
if (error.response?.status === 401 || error.response?.status === 403) {
alert('Your session has expired. Please log in again.');
return;
}
alert('Error rejecting device: ' + (error.response?.data?.message || error.message));
}
}
};
@@ -559,6 +569,14 @@ const DeviceModal = ({ device, onClose, onSave }) => {
onSave();
} catch (error) {
console.error('Error saving device:', error);
// Check if it's a token expiration error
if (error.response?.status === 401 || error.response?.status === 403) {
alert('Your session has expired. Please log in again.');
// The API interceptor will handle the logout automatically
return;
}
alert('Error saving device: ' + (error.response?.data?.message || error.message));
} finally {
setSaving(false);

View File

@@ -48,11 +48,16 @@ api.interceptors.request.use(
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Token expired or invalid - remove token and let ProtectedRoute handle navigation
localStorage.removeItem('token');
// Force a state update by dispatching a custom event
window.dispatchEvent(new CustomEvent('auth-logout'));
if (error.response?.status === 401 || error.response?.status === 403) {
// Check if it's a token-related error
const errorMessage = error.response?.data?.message || '';
if (errorMessage.includes('token') || errorMessage.includes('expired') || error.response?.status === 401) {
console.warn('🔐 Token expired or invalid - logging out');
// Token expired or invalid - remove token and let ProtectedRoute handle navigation
localStorage.removeItem('token');
// Force a state update by dispatching a custom event
window.dispatchEvent(new CustomEvent('auth-logout'));
}
}
return Promise.reject(error);
}

View File

@@ -252,7 +252,7 @@ router.post('/', authenticateToken, validateRequest(deviceSchema), async (req, r
}
});
// PUT /api/devices/:id - Update device (admin only)
// PUT /api/devices/:id - Update device
router.put('/:id', authenticateToken, validateRequest(updateDeviceSchema), async (req, res) => {
try {
const device = await Device.findByPk(req.params.id);
@@ -264,11 +264,16 @@ router.put('/:id', authenticateToken, validateRequest(updateDeviceSchema), async
});
}
console.log(`📝 Device ${req.params.id} update requested by user ${req.user.id} (${req.user.username})`);
console.log('Update data:', req.body);
await device.update(req.body);
// Emit real-time update
req.io.emit('device_updated', device);
console.log(`✅ Device ${req.params.id} updated successfully`);
res.json({
success: true,
data: device,

View File

@@ -1,293 +0,0 @@
const express = require('express');
const router = express.Router();
const Joi = require('joi');
const { validateRequest } = require('../middleware/validation');
const { Device, Heartbeat, DroneDetection } = require('../models');
const { Op } = require('sequelize');
// Validation schemas
const approveDeviceSchema = Joi.object({
device_id: Joi.number().integer().required(),
approved: Joi.boolean().required()
});
const updateDeviceSchema = Joi.object({
name: Joi.string().optional(),
geo_lat: Joi.number().min(-90).max(90).optional(),
geo_lon: Joi.number().min(-180).max(180).optional(),
location_description: Joi.string().optional(),
is_active: Joi.boolean().optional(),
heartbeat_interval: Joi.number().integer().min(30).optional(),
notes: Joi.string().allow('').optional()
});
// GET /api/devices - List all devices with approval status
router.get('/', async (req, res) => {
try {
const devices = await Device.findAll({
attributes: [
'id', 'name', 'geo_lat', 'geo_lon', 'location_description',
'is_active', 'is_approved', 'last_heartbeat', 'heartbeat_interval',
'firmware_version', 'installation_date', 'notes', 'created_at'
],
order: [['created_at', 'DESC']]
});
// Add status information
const devicesWithStatus = devices.map(device => {
const lastHeartbeat = device.last_heartbeat;
const heartbeatInterval = device.heartbeat_interval || 300;
const now = new Date();
const timeSinceHeartbeat = lastHeartbeat ?
(now - new Date(lastHeartbeat)) / 1000 : null;
let status = 'unknown';
if (!device.is_approved) {
status = 'pending_approval';
} else if (!device.is_active) {
status = 'inactive';
} else if (timeSinceHeartbeat && timeSinceHeartbeat > heartbeatInterval * 2) {
status = 'offline';
} else if (timeSinceHeartbeat && timeSinceHeartbeat <= heartbeatInterval * 2) {
status = 'online';
}
return {
...device.toJSON(),
status,
time_since_heartbeat: timeSinceHeartbeat
};
});
res.json({
success: true,
data: devicesWithStatus
});
} catch (error) {
console.error('Error fetching devices:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch devices'
});
}
});
// GET /api/devices/pending - List devices pending approval
router.get('/pending', async (req, res) => {
try {
const pendingDevices = await Device.findAll({
where: { is_approved: false },
attributes: [
'id', 'name', 'geo_lat', 'geo_lon', 'last_heartbeat',
'created_at', 'firmware_version'
],
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: pendingDevices
});
} catch (error) {
console.error('Error fetching pending devices:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch pending devices'
});
}
});
// POST /api/devices/:id/approve - Approve or reject a device
router.post('/:id/approve', validateRequest(approveDeviceSchema), async (req, res) => {
try {
const deviceId = parseInt(req.params.id);
const { approved } = req.body;
const device = await Device.findByPk(deviceId);
if (!device) {
return res.status(404).json({
success: false,
error: 'Device not found'
});
}
await device.update({ is_approved: approved });
// Emit real-time notification
const { io } = require('../index');
if (io) {
io.emit('device_approval_updated', {
device_id: deviceId,
approved: approved,
timestamp: new Date().toISOString(),
message: approved ?
`Device ${deviceId} has been approved` :
`Device ${deviceId} approval has been revoked`
});
}
console.log(`${approved ? '✅' : '❌'} Device ${deviceId} approval ${approved ? 'granted' : 'revoked'}`);
res.json({
success: true,
data: device,
message: approved ? 'Device approved successfully' : 'Device approval revoked'
});
} catch (error) {
console.error('Error updating device approval:', error);
res.status(500).json({
success: false,
error: 'Failed to update device approval'
});
}
});
// PUT /api/devices/:id - Update device information
router.put('/:id', validateRequest(updateDeviceSchema), async (req, res) => {
try {
const deviceId = parseInt(req.params.id);
const updateData = req.body;
const device = await Device.findByPk(deviceId);
if (!device) {
return res.status(404).json({
success: false,
error: 'Device not found'
});
}
await device.update(updateData);
// Emit real-time notification
const { io } = require('../index');
if (io) {
io.emit('device_updated', {
device_id: deviceId,
updates: updateData,
timestamp: new Date().toISOString()
});
}
console.log(`📝 Device ${deviceId} updated`);
res.json({
success: true,
data: device,
message: 'Device updated successfully'
});
} catch (error) {
console.error('Error updating device:', error);
res.status(500).json({
success: false,
error: 'Failed to update device'
});
}
});
// DELETE /api/devices/:id - Delete a device and all its data
router.delete('/:id', async (req, res) => {
try {
const deviceId = parseInt(req.params.id);
const device = await Device.findByPk(deviceId);
if (!device) {
return res.status(404).json({
success: false,
error: 'Device not found'
});
}
// Delete related data first (due to foreign key constraints)
await DroneDetection.destroy({ where: { device_id: deviceId } });
await Heartbeat.destroy({ where: { device_id: deviceId } });
// Delete the device
await device.destroy();
// Emit real-time notification
const { io } = require('../index');
if (io) {
io.emit('device_deleted', {
device_id: deviceId,
timestamp: new Date().toISOString(),
message: `Device ${deviceId} has been deleted`
});
}
console.log(`🗑️ Device ${deviceId} and all related data deleted`);
res.json({
success: true,
message: 'Device deleted successfully'
});
} catch (error) {
console.error('Error deleting device:', error);
res.status(500).json({
success: false,
error: 'Failed to delete device'
});
}
});
// GET /api/devices/:id/stats - Get device statistics
router.get('/:id/stats', async (req, res) => {
try {
const deviceId = parseInt(req.params.id);
const days = parseInt(req.query.days) || 7;
const device = await Device.findByPk(deviceId);
if (!device) {
return res.status(404).json({
success: false,
error: 'Device not found'
});
}
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
// Get heartbeat count
const heartbeatCount = await Heartbeat.count({
where: {
device_id: deviceId,
received_at: { [Op.gte]: startDate }
}
});
// Get detection count
const detectionCount = await DroneDetection.count({
where: {
device_id: deviceId,
server_timestamp: { [Op.gte]: startDate }
}
});
// Get latest heartbeat
const latestHeartbeat = await Heartbeat.findOne({
where: { device_id: deviceId },
order: [['received_at', 'DESC']],
attributes: ['received_at', 'signal_strength', 'battery_level', 'temperature']
});
res.json({
success: true,
data: {
device_id: deviceId,
period_days: days,
heartbeat_count: heartbeatCount,
detection_count: detectionCount,
latest_heartbeat: latestHeartbeat,
uptime_percentage: heartbeatCount > 0 ?
Math.min(100, (heartbeatCount / (days * 24 * 12)) * 100) : 0 // Assuming 5min intervals
}
});
} catch (error) {
console.error('Error fetching device stats:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch device statistics'
});
}
});
module.exports = router;