Fix jwt-token

This commit is contained in:
2025-09-22 10:42:25 +02:00
parent 53873812a6
commit 19599047c9
2 changed files with 197 additions and 49 deletions

View File

@@ -0,0 +1,181 @@
import { useState, useEffect, useMemo } from 'react';
/**
* Custom hook for fetching and managing drone types from the API
* Provides caching, error handling, and convenient access to drone type data
*/
export const useDroneTypes = () => {
const [droneTypes, setDroneTypes] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Fetch drone types from API
useEffect(() => {
const fetchDroneTypes = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch('/api/drone-types');
if (!response.ok) {
throw new Error(`Failed to fetch drone types: ${response.statusText}`);
}
const result = await response.json();
if (result.success && result.data) {
setDroneTypes(result.data);
} else {
throw new Error('Invalid response format from drone types API');
}
} catch (err) {
console.error('Error fetching drone types:', err);
setError(err.message);
// Set fallback data on error
setDroneTypes(getFallbackDroneTypes());
} finally {
setLoading(false);
}
};
fetchDroneTypes();
}, []);
// Create a mapping object for quick lookups by ID
const droneTypeMap = useMemo(() => {
const map = {};
droneTypes.forEach(type => {
map[type.id] = type;
});
return map;
}, [droneTypes]);
// Get drone type info by ID with fallback
const getDroneTypeInfo = (droneTypeId) => {
const typeInfo = droneTypeMap[droneTypeId];
if (typeInfo) {
return {
...typeInfo,
// Add visual styling based on threat level
...getVisualStyling(typeInfo)
};
}
// Fallback for unknown types
return {
id: droneTypeId,
name: 'Unknown',
category: 'Unknown',
threat_level: 'medium',
description: `Unknown drone type ${droneTypeId}`,
...getVisualStyling({ threat_level: 'medium' })
};
};
// Get drone types by category
const getDroneTypesByCategory = (category) => {
return droneTypes.filter(type => type.category === category);
};
// Get drone types by threat level
const getDroneTypesByThreatLevel = (threatLevel) => {
return droneTypes.filter(type => type.threat_level === threatLevel);
};
// Get all categories
const getCategories = () => {
const categories = new Set(droneTypes.map(type => type.category));
return Array.from(categories);
};
// Get all threat levels
const getThreatLevels = () => {
const levels = new Set(droneTypes.map(type => type.threat_level));
return Array.from(levels);
};
return {
droneTypes,
droneTypeMap,
loading,
error,
getDroneTypeInfo,
getDroneTypesByCategory,
getDroneTypesByThreatLevel,
getCategories,
getThreatLevels
};
};
/**
* Get visual styling based on threat level and category
*/
const getVisualStyling = (typeInfo) => {
const isMilitary = typeInfo.category?.includes('Military') ||
typeInfo.threat_level === 'critical';
const isWarning = isMilitary || typeInfo.threat_level === 'high';
// Base styling
let styling = {
warning: isWarning,
icon: isMilitary ? '⚠️' : '🛩️'
};
// Color scheme based on threat level
switch (typeInfo.threat_level) {
case 'critical':
styling = {
...styling,
color: 'red',
bgColor: 'bg-red-100',
textColor: 'text-red-800',
borderColor: 'border-red-300'
};
break;
case 'high':
styling = {
...styling,
color: 'orange',
bgColor: 'bg-orange-100',
textColor: 'text-orange-800',
borderColor: 'border-orange-300'
};
break;
case 'medium':
styling = {
...styling,
color: 'yellow',
bgColor: 'bg-yellow-100',
textColor: 'text-yellow-800',
borderColor: 'border-yellow-300'
};
break;
case 'low':
default:
styling = {
...styling,
color: 'gray',
bgColor: 'bg-gray-100',
textColor: 'text-gray-800',
borderColor: 'border-gray-300'
};
break;
}
return styling;
};
/**
* Fallback drone types for when API is unavailable
* This ensures the app continues working even if the API is down
*/
const getFallbackDroneTypes = () => {
return [
{ id: 0, name: 'None', category: 'Unknown', threat_level: 'low' },
{ id: 1, name: 'Unknown', category: 'Unknown', threat_level: 'medium' },
{ id: 2, name: 'Orlan', category: 'Military/Reconnaissance', threat_level: 'critical' },
{ id: 3, name: 'Zala', category: 'Military/Surveillance', threat_level: 'critical' },
{ id: 13, name: 'DJI', category: 'Commercial/Professional', threat_level: 'low' }
];
};

View File

@@ -12,8 +12,12 @@ import {
ChevronRightIcon
} from '@heroicons/react/24/outline';
import { EditAlertModal, DetectionDetailsModal } from '../components/AlertModals';
import { useDroneTypes } from '../hooks/useDroneTypes';
const Alerts = () => {
// Drone types hook for dynamic drone type data
const { getDroneTypeInfo: getDroneTypeInfoFromAPI, loading: droneTypesLoading } = useDroneTypes();
const [alertRules, setAlertRules] = useState([]);
const [alertLogs, setAlertLogs] = useState([]);
const [alertStats, setAlertStats] = useState(null);
@@ -52,6 +56,9 @@ const Alerts = () => {
}
};
// Show loading if either alerts or drone types are loading
const isLoading = loading || droneTypesLoading;
// Group alerts by alert_event_id to show related alerts together
const groupAlertsByEvent = (logs) => {
const grouped = {};
@@ -89,35 +96,14 @@ const Alerts = () => {
setExpandedGroups(newExpanded);
};
// Get drone type information with visual styling
// Get drone type information with visual styling (now using dynamic API data)
const getDroneTypeInfo = (detection) => {
if (!detection || detection.drone_type === undefined) {
return { name: 'Unknown', color: 'gray', bgColor: 'bg-gray-100', textColor: 'text-gray-600', icon: '🔍' };
}
// Map drone types based on the backend droneTypes utility
const droneTypeMap = {
0: { name: 'Unknown', color: 'gray', bgColor: 'bg-gray-100', textColor: 'text-gray-600', icon: '🔍' },
1: { name: 'Generic', color: 'blue', bgColor: 'bg-blue-100', textColor: 'text-blue-800', icon: '🚁' },
2: { name: 'Orlan', color: 'red', bgColor: 'bg-red-100', textColor: 'text-red-800', icon: '⚠️', warning: '⚠️ MILITARY' },
3: { name: 'Zala', color: 'orange', bgColor: 'bg-orange-100', textColor: 'text-orange-800', icon: '🔶', warning: '⚠️ MILITARY' },
4: { name: 'Forpost', color: 'purple', bgColor: 'bg-purple-100', textColor: 'text-purple-800', icon: '🟣', warning: '⚠️ MILITARY' },
5: { name: 'Inokhodets', color: 'indigo', bgColor: 'bg-indigo-100', textColor: 'text-indigo-800', icon: '🔷', warning: '⚠️ MILITARY' },
6: { name: 'Lancet', color: 'red', bgColor: 'bg-red-200', textColor: 'text-red-900', icon: '💥', warning: '⚠️ MILITARY' },
7: { name: 'Shahed', color: 'yellow', bgColor: 'bg-yellow-100', textColor: 'text-yellow-800', icon: '⚡', warning: '⚠️ MILITARY' },
8: { name: 'Geran', color: 'amber', bgColor: 'bg-amber-100', textColor: 'text-amber-800', icon: '🟨', warning: '⚠️ MILITARY' },
9: { name: 'Kub', color: 'green', bgColor: 'bg-green-100', textColor: 'text-green-800', icon: '🟢', warning: '⚠️ MILITARY' },
10: { name: 'X-UAV', color: 'teal', bgColor: 'bg-teal-100', textColor: 'text-teal-800', icon: '🔷' },
11: { name: 'SuperCam', color: 'cyan', bgColor: 'bg-cyan-100', textColor: 'text-cyan-800', icon: '📷' },
12: { name: 'Eleron', color: 'lime', bgColor: 'bg-lime-100', textColor: 'text-lime-800', icon: '🟩' },
13: { name: 'DJI', color: 'blue', bgColor: 'bg-blue-200', textColor: 'text-blue-900', icon: '📱' },
14: { name: 'Autel', color: 'violet', bgColor: 'bg-violet-100', textColor: 'text-violet-800', icon: '🟪' },
15: { name: 'Parrot', color: 'emerald', bgColor: 'bg-emerald-100', textColor: 'text-emerald-800', icon: '🦜' },
16: { name: 'Skydio', color: 'sky', bgColor: 'bg-sky-100', textColor: 'text-sky-800', icon: '☁️' },
17: { name: 'CryptoOrlan', color: 'red', bgColor: 'bg-red-300', textColor: 'text-red-900', icon: '🔴', warning: '⚠️ MILITARY' }
};
return droneTypeMap[detection.drone_type] || droneTypeMap[0];
// Use the dynamic API data from the hook
return getDroneTypeInfoFromAPI(detection.drone_type);
};
const handleDeleteRule = async (ruleId) => {
@@ -174,11 +160,13 @@ const Alerts = () => {
}
};
if (loading) {
if (isLoading) {
return (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
<span className="ml-4 text-gray-600">{t('alerts.loading')}</span>
<span className="ml-4 text-gray-600">
{droneTypesLoading ? 'Loading drone types...' : t('alerts.loading')}
</span>
</div>
);
}
@@ -338,29 +326,8 @@ const Alerts = () => {
<div className="text-xs text-gray-500">{t('alerts.droneTypes')}:</div>
<div className="flex flex-wrap gap-1 mt-1">
{rule.drone_types.map((typeId, index) => {
// Use the same comprehensive drone type mapping as getDroneTypeInfo
const droneTypeMap = {
0: { name: 'Unknown', color: 'gray' },
1: { name: 'Generic', color: 'blue' },
2: { name: 'Orlan', color: 'red', warning: true },
3: { name: 'Zala', color: 'orange', warning: true },
4: { name: 'Forpost', color: 'purple', warning: true },
5: { name: 'Inokhodets', color: 'indigo', warning: true },
6: { name: 'Lancet', color: 'red', warning: true },
7: { name: 'Shahed', color: 'yellow', warning: true },
8: { name: 'Geran', color: 'amber', warning: true },
9: { name: 'Kub', color: 'green', warning: true },
10: { name: 'X-UAV', color: 'teal' },
11: { name: 'SuperCam', color: 'cyan' },
12: { name: 'Eleron', color: 'lime' },
13: { name: 'DJI', color: 'blue' },
14: { name: 'Autel', color: 'violet' },
15: { name: 'Parrot', color: 'emerald' },
16: { name: 'Skydio', color: 'sky' },
17: { name: 'CryptoOrlan', color: 'red', warning: true }
};
const droneInfo = droneTypeMap[typeId] || droneTypeMap[0];
// Use the dynamic API data instead of hardcoded mapping
const droneInfo = getDroneTypeInfoFromAPI(typeId);
return (
<span