Fix jwt-token

This commit is contained in:
2025-09-10 06:51:54 +02:00
parent 9ad21bc1f5
commit ad3b95646a
5 changed files with 326 additions and 0 deletions

View File

@@ -1,2 +1,5 @@
# Development environment - runs on root path
VITE_BASE_PATH=
# Debug configuration
VITE_DEBUG_ENABLED=true

View File

@@ -1,2 +1,5 @@
# Production environment - deployed under /uggla/ path
VITE_BASE_PATH=/uggla/
# Debug configuration
VITE_DEBUG_ENABLED=true

View File

@@ -0,0 +1,282 @@
import React, { useState, useEffect } from 'react';
import api from '../services/api';
import { format } from 'date-fns';
import {
XMarkIcon,
BugAntIcon,
EyeIcon,
ChevronDownIcon,
ChevronUpIcon,
ArrowPathIcon,
ExclamationTriangleIcon
} from '@heroicons/react/24/outline';
const DebugOverlay = ({ isOpen, onClose }) => {
const [activeTab, setActiveTab] = useState('detections');
const [detectionPayloads, setDetectionPayloads] = useState([]);
const [heartbeatPayloads, setHeartbeatPayloads] = useState([]);
const [debugConfig, setDebugConfig] = useState({});
const [loading, setLoading] = useState(false);
const [expandedItems, setExpandedItems] = useState(new Set());
const [filters, setFilters] = useState({
limit: 20,
device_id: ''
});
useEffect(() => {
if (isOpen) {
fetchDebugConfig();
fetchPayloads();
}
}, [isOpen, activeTab, filters]);
const fetchDebugConfig = async () => {
try {
const response = await api.get('/debug/config');
if (response.data.success) {
setDebugConfig(response.data.config);
}
} catch (error) {
console.error('Error fetching debug config:', error);
}
};
const fetchPayloads = async () => {
setLoading(true);
try {
const endpoint = activeTab === 'detections' ? '/debug/detection-payloads' : '/debug/heartbeat-payloads';
const params = new URLSearchParams();
if (filters.limit) params.append('limit', filters.limit);
if (filters.device_id) params.append('device_id', filters.device_id);
const response = await api.get(`${endpoint}?${params}`);
if (response.data.success) {
if (activeTab === 'detections') {
setDetectionPayloads(response.data.data);
} else {
setHeartbeatPayloads(response.data.data);
}
}
} catch (error) {
console.error('Error fetching payloads:', error);
} finally {
setLoading(false);
}
};
const toggleExpanded = (id) => {
const newExpanded = new Set(expandedItems);
if (newExpanded.has(id)) {
newExpanded.delete(id);
} else {
newExpanded.add(id);
}
setExpandedItems(newExpanded);
};
const formatPayload = (payload) => {
return JSON.stringify(payload, null, 2);
};
const currentData = activeTab === 'detections' ? detectionPayloads : heartbeatPayloads;
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 overflow-hidden">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black bg-opacity-50"
onClick={onClose}
/>
{/* Overlay Panel */}
<div className="absolute right-0 top-0 h-full w-full max-w-4xl bg-white shadow-xl">
<div className="flex h-full flex-col">
{/* Header */}
<div className="border-b border-gray-200 bg-gray-50 px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<BugAntIcon className="h-6 w-6 text-gray-600" />
<h2 className="text-lg font-semibold text-gray-900">Debug Payload Viewer</h2>
{debugConfig.STORE_RAW_PAYLOAD && (
<span className="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800">
Active
</span>
)}
</div>
<button
onClick={onClose}
className="rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
>
<XMarkIcon className="h-6 w-6" />
</button>
</div>
{/* Tabs */}
<div className="mt-4 flex space-x-4">
<button
onClick={() => setActiveTab('detections')}
className={`px-4 py-2 text-sm font-medium rounded-md ${
activeTab === 'detections'
? 'bg-blue-100 text-blue-700'
: 'text-gray-500 hover:text-gray-700'
}`}
>
Detection Payloads ({detectionPayloads.length})
</button>
<button
onClick={() => setActiveTab('heartbeats')}
className={`px-4 py-2 text-sm font-medium rounded-md ${
activeTab === 'heartbeats'
? 'bg-blue-100 text-blue-700'
: 'text-gray-500 hover:text-gray-700'
}`}
>
Heartbeat Payloads ({heartbeatPayloads.length})
</button>
</div>
</div>
{/* Controls */}
<div className="border-b border-gray-200 bg-gray-50 px-6 py-3">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<label className="text-sm font-medium text-gray-700">Limit:</label>
<select
value={filters.limit}
onChange={(e) => setFilters({...filters, limit: e.target.value})}
className="rounded border-gray-300 text-sm"
>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<div className="flex items-center space-x-2">
<label className="text-sm font-medium text-gray-700">Device ID:</label>
<input
type="text"
value={filters.device_id}
onChange={(e) => setFilters({...filters, device_id: e.target.value})}
placeholder="Filter by device ID"
className="rounded border-gray-300 text-sm w-40"
/>
</div>
<button
onClick={fetchPayloads}
disabled={loading}
className="flex items-center space-x-1 rounded bg-blue-600 px-3 py-1 text-sm text-white hover:bg-blue-700 disabled:opacity-50"
>
<ArrowPathIcon className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
<span>Refresh</span>
</button>
</div>
</div>
{/* Content */}
<div className="flex-1 overflow-auto">
{!debugConfig.STORE_RAW_PAYLOAD && (
<div className="p-6">
<div className="rounded-md bg-yellow-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<ExclamationTriangleIcon className="h-5 w-5 text-yellow-400" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">
Raw Payload Storage Disabled
</h3>
<div className="mt-2 text-sm text-yellow-700">
<p>Set STORE_RAW_PAYLOAD=true in environment variables to enable payload storage.</p>
</div>
</div>
</div>
</div>
</div>
)}
{loading && (
<div className="flex items-center justify-center p-8">
<ArrowPathIcon className="h-8 w-8 animate-spin text-gray-400" />
</div>
)}
{!loading && currentData.length === 0 && (
<div className="p-6 text-center text-gray-500">
No {activeTab} payloads found
</div>
)}
<div className="space-y-2 p-4">
{currentData.map((item) => (
<div key={item.id} className="rounded-lg border border-gray-200 bg-white">
<div
className="flex items-center justify-between p-4 cursor-pointer hover:bg-gray-50"
onClick={() => toggleExpanded(item.id)}
>
<div className="flex items-center space-x-4">
<span className="text-sm font-medium text-gray-900">
Device {item.device_id}
</span>
{activeTab === 'detections' && (
<>
<span className="text-sm text-gray-500">
Drone {item.drone_id} | Type {item.drone_type}
</span>
<span className="text-sm text-gray-500">
RSSI: {item.rssi}dBm
</span>
</>
)}
<span className="text-sm text-gray-500">
{format(new Date(item.server_timestamp || item.received_at), 'MMM dd, HH:mm:ss')}
</span>
</div>
<div className="flex items-center space-x-2">
{item.raw_payload && (
<span className="inline-flex items-center rounded-full bg-green-100 px-2 py-1 text-xs font-medium text-green-800">
Raw Data
</span>
)}
{expandedItems.has(item.id) ? (
<ChevronUpIcon className="h-5 w-5 text-gray-400" />
) : (
<ChevronDownIcon className="h-5 w-5 text-gray-400" />
)}
</div>
</div>
{expandedItems.has(item.id) && (
<div className="border-t border-gray-200 p-4">
{item.raw_payload ? (
<div>
<h4 className="text-sm font-medium text-gray-900 mb-2">Raw Payload:</h4>
<pre className="text-xs bg-gray-50 p-3 rounded overflow-auto max-h-96">
{formatPayload(item.raw_payload)}
</pre>
</div>
) : (
<div className="text-sm text-gray-500">
No raw payload stored for this item
</div>
)}
</div>
)}
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default DebugOverlay;

View File

@@ -0,0 +1,34 @@
import React, { useState } from 'react';
import { BugAntIcon } from '@heroicons/react/24/outline';
import DebugOverlay from './DebugOverlay';
const DebugToggle = () => {
const [isDebugOpen, setIsDebugOpen] = useState(false);
// Only show in development or when debug is enabled
const shouldShow = import.meta.env.DEV ||
import.meta.env.VITE_DEBUG_ENABLED === 'true';
if (!shouldShow) return null;
return (
<>
{/* Floating Debug Button */}
<button
onClick={() => setIsDebugOpen(true)}
className="fixed bottom-4 right-4 z-40 flex h-12 w-12 items-center justify-center rounded-full bg-gray-800 text-white shadow-lg hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
title="Open Debug Panel"
>
<BugAntIcon className="h-6 w-6" />
</button>
{/* Debug Overlay */}
<DebugOverlay
isOpen={isDebugOpen}
onClose={() => setIsDebugOpen(false)}
/>
</>
);
};
export default DebugToggle;

View File

@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Outlet, Link, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { useSocket } from '../contexts/SocketContext';
import DebugToggle from './DebugToggle';
import {
HomeIcon,
MapIcon,
@@ -148,6 +149,9 @@ const Layout = () => {
</div>
</div>
</main>
{/* Debug Toggle (floating button) */}
<DebugToggle />
</div>
</div>
);