Fix jwt-token

This commit is contained in:
2025-09-19 15:02:01 +02:00
parent 1c4e06515e
commit 5d21bb5b0a
4 changed files with 84 additions and 24 deletions

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { useSocket } from '../contexts/SocketContext'; import { useSocket } from '../contexts/SocketContext';
import { useTranslation } from '../utils/tempTranslations';
import { import {
ExclamationTriangleIcon, ExclamationTriangleIcon,
InformationCircleIcon, InformationCircleIcon,
@@ -13,6 +14,7 @@ import {
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
const MovementAlertsPanel = () => { const MovementAlertsPanel = () => {
const { t } = useTranslation();
const { movementAlerts, clearMovementAlerts } = useSocket(); const { movementAlerts, clearMovementAlerts } = useSocket();
const [expandedAlert, setExpandedAlert] = useState(null); const [expandedAlert, setExpandedAlert] = useState(null);
const [filter, setFilter] = useState('all'); // all, critical, high, medium const [filter, setFilter] = useState('all'); // all, critical, high, medium
@@ -55,12 +57,12 @@ const MovementAlertsPanel = () => {
}); });
const droneTypes = { const droneTypes = {
1: "DJI Mavic", 1: t('movementAlerts.droneTypes.djiMavic'),
2: "Racing Drone", 2: t('movementAlerts.droneTypes.racingDrone'),
3: "DJI Phantom", 3: t('movementAlerts.droneTypes.djiPhantom'),
4: "Fixed Wing", 4: t('movementAlerts.droneTypes.fixedWing'),
5: "Surveillance", 5: t('movementAlerts.droneTypes.surveillance'),
0: "Unknown" 0: t('movementAlerts.droneTypes.unknown')
}; };
return ( return (
@@ -68,7 +70,7 @@ const MovementAlertsPanel = () => {
<div className="px-6 py-4 border-b border-gray-200"> <div className="px-6 py-4 border-b border-gray-200">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<h3 className="text-lg font-medium text-gray-900">Movement Alerts</h3> <h3 className="text-lg font-medium text-gray-900">{t('movementAlerts.title')}</h3>
{movementAlerts.length > 0 && ( {movementAlerts.length > 0 && (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800"> <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
{movementAlerts.length} {movementAlerts.length}
@@ -82,10 +84,10 @@ const MovementAlertsPanel = () => {
onChange={(e) => setFilter(e.target.value)} onChange={(e) => setFilter(e.target.value)}
className="text-sm border border-gray-300 rounded px-2 py-1" className="text-sm border border-gray-300 rounded px-2 py-1"
> >
<option value="all">All Alerts</option> <option value="all">{t('movementAlerts.allAlerts')}</option>
<option value="critical">Critical</option> <option value="critical">{t('movementAlerts.critical')}</option>
<option value="high">High Priority</option> <option value="high">{t('movementAlerts.highPriority')}</option>
<option value="medium">Medium Priority</option> <option value="medium">{t('movementAlerts.mediumPriority')}</option>
</select> </select>
{movementAlerts.length > 0 && ( {movementAlerts.length > 0 && (
@@ -93,7 +95,7 @@ const MovementAlertsPanel = () => {
onClick={clearMovementAlerts} onClick={clearMovementAlerts}
className="text-sm text-gray-600 hover:text-gray-800" className="text-sm text-gray-600 hover:text-gray-800"
> >
Clear All {t('movementAlerts.clearAll')}
</button> </button>
)} )}
</div> </div>

View File

@@ -340,7 +340,7 @@ const MapView = () => {
onChange={(e) => setShowDroneDetections(e.target.checked)} onChange={(e) => setShowDroneDetections(e.target.checked)}
className="rounded border-gray-300 text-primary-600 focus:ring-primary-500" className="rounded border-gray-300 text-primary-600 focus:ring-primary-500"
/> />
<span className="text-sm text-gray-700">Show Drone Detections</span> <span className="text-sm text-gray-700">{t('map.showDroneDetections')}</span>
</label> </label>
{droneDetectionHistory.length > 0 && ( {droneDetectionHistory.length > 0 && (

View File

@@ -1,11 +1,13 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Navigate, Link } from 'react-router-dom'; import { Navigate, Link } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { useTranslation } from '../utils/tempTranslations';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'; import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import api from '../services/api'; import api from '../services/api';
const Register = () => { const Register = () => {
const { t } = useTranslation();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
username: '', username: '',
email: '', email: '',
@@ -31,11 +33,11 @@ const Register = () => {
// Security check: If registration is not enabled, show error // Security check: If registration is not enabled, show error
if (!response.data.data?.features?.registration) { if (!response.data.data?.features?.registration) {
toast.error('Registration is not enabled for this tenant'); toast.error(t('register.registrationDisabled'));
} }
} catch (error) { } catch (error) {
console.error('Failed to fetch tenant config:', error); console.error('Failed to fetch tenant config:', error);
toast.error('Failed to load authentication configuration'); toast.error(t('register.configLoadFailed'));
} finally { } finally {
setConfigLoading(false); setConfigLoading(false);
} }
@@ -124,38 +126,38 @@ const Register = () => {
// Validation // Validation
if (!formData.username || !formData.email || !formData.password) { if (!formData.username || !formData.email || !formData.password) {
toast.error('Please fill in all required fields'); toast.error(t('register.fillAllFields'));
return; return;
} }
if (formData.password !== formData.confirmPassword) { if (formData.password !== formData.confirmPassword) {
toast.error('Passwords do not match'); toast.error(t('register.passwordsMismatch'));
return; return;
} }
if (formData.password.length < 8) { if (formData.password.length < 8) {
toast.error('Password must be at least 8 characters long'); toast.error(t('register.passwordTooShort'));
return; return;
} }
// Strong password validation // Strong password validation
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/; const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/;
if (!passwordRegex.test(formData.password)) { if (!passwordRegex.test(formData.password)) {
toast.error('Password must contain at least one lowercase letter, one uppercase letter, and one number'); toast.error(t('register.passwordRequirements'));
return; return;
} }
// Username validation // Username validation
const usernameRegex = /^[a-zA-Z0-9._-]+$/; const usernameRegex = /^[a-zA-Z0-9._-]+$/;
if (!usernameRegex.test(formData.username)) { if (!usernameRegex.test(formData.username)) {
toast.error('Username can only contain letters, numbers, dots, underscores, and hyphens'); toast.error(t('register.usernameInvalid'));
return; return;
} }
// Email validation // Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email)) { if (!emailRegex.test(formData.email)) {
toast.error('Please enter a valid email address'); toast.error(t('register.emailInvalid'));
return; return;
} }
@@ -163,7 +165,7 @@ const Register = () => {
if (formData.phone_number) { if (formData.phone_number) {
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/; const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
if (!phoneRegex.test(formData.phone_number.replace(/[\s\-\(\)]/g, ''))) { if (!phoneRegex.test(formData.phone_number.replace(/[\s\-\(\)]/g, ''))) {
toast.error('Please enter a valid phone number'); toast.error(t('register.phoneInvalid'));
return; return;
} }
} }

View File

@@ -313,7 +313,35 @@ const translations = {
differentPatterns: 'Different dash patterns', differentPatterns: 'Different dash patterns',
droneLabels: 'Drone ID labels shown', droneLabels: 'Drone ID labels shown',
positionOffsets: 'Slight position offsets for visibility', positionOffsets: 'Slight position offsets for visibility',
ringSize: 'Ring size = estimated distance from detector' ringSize: 'Ring size = estimated distance from detector',
showDroneDetections: 'Show Drone Detections'
},
movementAlerts: {
title: 'Movement Alerts',
allAlerts: 'All Alerts',
critical: 'Critical',
highPriority: 'High Priority',
mediumPriority: 'Medium Priority',
clearAll: 'Clear All',
droneTypes: {
djiMavic: 'DJI Mavic',
racingDrone: 'Racing Drone',
djiPhantom: 'DJI Phantom',
fixedWing: 'Fixed Wing',
surveillance: 'Surveillance',
unknown: 'Unknown'
}
},
register: {
registrationDisabled: 'Registration is not enabled for this tenant',
configLoadFailed: 'Failed to load authentication configuration',
fillAllFields: 'Please fill in all required fields',
passwordsMismatch: 'Passwords do not match',
passwordTooShort: 'Password must be at least 8 characters long',
passwordRequirements: 'Password must contain at least one lowercase letter, one uppercase letter, and one number',
usernameInvalid: 'Username can only contain letters, numbers, dots, underscores, and hyphens',
emailInvalid: 'Please enter a valid email address',
phoneInvalid: 'Please enter a valid phone number'
}, },
app: { app: {
title: 'UAM-ILS Drone Detection System', title: 'UAM-ILS Drone Detection System',
@@ -684,7 +712,35 @@ const translations = {
differentPatterns: 'Olika streckmönster', differentPatterns: 'Olika streckmönster',
droneLabels: 'Drönar-ID-etiketter visas', droneLabels: 'Drönar-ID-etiketter visas',
positionOffsets: 'Lätta positionsförskjutningar för synlighet', positionOffsets: 'Lätta positionsförskjutningar för synlighet',
ringSize: 'Ringstorlek = uppskattad distans från detektor' ringSize: 'Ringstorlek = uppskattad distans från detektor',
showDroneDetections: 'Visa drönardetekteringar'
},
movementAlerts: {
title: 'Rörelselarm',
allAlerts: 'Alla larm',
critical: 'Kritisk',
highPriority: 'Hög prioritet',
mediumPriority: 'Medel prioritet',
clearAll: 'Rensa alla',
droneTypes: {
djiMavic: 'DJI Mavic',
racingDrone: 'Racingdrönare',
djiPhantom: 'DJI Phantom',
fixedWing: 'Fast vinge',
surveillance: 'Övervakning',
unknown: 'Okänd'
}
},
register: {
registrationDisabled: 'Registrering är inte aktiverad för denna klient',
configLoadFailed: 'Misslyckades med att ladda autentiseringskonfiguration',
fillAllFields: 'Vänligen fyll i alla obligatoriska fält',
passwordsMismatch: 'Lösenorden stämmer inte överens',
passwordTooShort: 'Lösenordet måste vara minst 8 tecken långt',
passwordRequirements: 'Lösenordet måste innehålla minst en liten bokstav, en stor bokstav och en siffra',
usernameInvalid: 'Användarnamn kan bara innehålla bokstäver, siffror, punkter, understreck och bindestreck',
emailInvalid: 'Vänligen ange en giltig e-postadress',
phoneInvalid: 'Vänligen ange ett giltigt telefonnummer'
}, },
app: { app: {
title: 'UAM-ILS Drönardetekteringssystem', title: 'UAM-ILS Drönardetekteringssystem',