import React, { useEffect, useState } from 'react'; import './Dashboard.css'; import { Shield, Users, Activity, Clock } from 'lucide-react'; interface Stats { total_logs: number; unique_attackers: number; active_deckies: number; deployed_deckies: number; } interface LogEntry { id: number; timestamp: string; decky: string; service: string; event_type: string | null; attacker_ip: string; raw_line: string; fields: string | null; msg: string | null; } interface DashboardProps { searchQuery: string; } const Dashboard: React.FC = ({ searchQuery }) => { const [stats, setStats] = useState(null); const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const token = localStorage.getItem('token'); const baseUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1'; let url = `${baseUrl}/stream?token=${token}`; if (searchQuery) { url += `&search=${encodeURIComponent(searchQuery)}`; } const eventSource = new EventSource(url); eventSource.onmessage = (event) => { try { const payload = JSON.parse(event.data); if (payload.type === 'logs') { setLogs(prev => [...payload.data, ...prev].slice(0, 100)); } else if (payload.type === 'stats') { setStats(payload.data); setLoading(false); } } catch (err) { console.error('Failed to parse SSE payload', err); } }; eventSource.onerror = (err) => { console.error('SSE connection error, attempting to reconnect...', err); }; return () => { eventSource.close(); }; }, [searchQuery]); if (loading && !stats) return
INITIALIZING SENSORS...
; return (
} label="TOTAL INTERACTIONS" value={stats?.total_logs || 0} /> } label="UNIQUE ATTACKERS" value={stats?.unique_attackers || 0} /> } label="ACTIVE DECKIES" value={`${stats?.active_deckies || 0} / ${stats?.deployed_deckies || 0}`} />

LIVE INTERACTION LOG

{logs.length > 0 ? logs.map(log => { let parsedFields: Record = {}; if (log.fields) { try { parsedFields = JSON.parse(log.fields); } catch (e) { // Ignore parsing errors } } return ( ); }) : ( )}
TIMESTAMP DECKY SERVICE ATTACKER IP EVENT
{new Date(log.timestamp).toLocaleString()} {log.decky} {log.service} {log.attacker_ip}
{log.event_type} {log.msg && log.msg !== '-' && — {log.msg}}
{Object.keys(parsedFields).length > 0 && (
{Object.entries(parsedFields).map(([k, v]) => ( {k}: {v} ))}
)}
NO INTERACTION DETECTED
); }; interface StatCardProps { icon: React.ReactNode; label: string; value: string | number; } const StatCard: React.FC = ({ icon, label, value }) => (
{icon}
{label} {value.toLocaleString()}
); export default Dashboard;