admin / SAynapse-Horizon
publicSelf Hosted Cyber Threat Intelligence Hub
SAynapse-Horizon / Synapse-Horizon-v2 / src / App.jsx
3630 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | import { useMemo, useState } from 'react' import { AlertTriangle, X } from 'lucide-react' import { useFeeds } from './hooks/useFeeds' import { SOURCES } from './data/sources' import Header from './components/Header' import Sidebar from './components/Sidebar' import MetricsRow from './components/MetricsRow' import CriticalTriagePanel from './components/CriticalTriagePanel' import CTIFeed from './components/CTIFeed' import DetailsDrawer from './components/DetailsDrawer' export default function App() { const feeds = useFeeds() const [selected, setSelected] = useState(null) const [dismissedError, setDismissedError] = useState(null) const showError = feeds.error && feeds.error !== dismissedError // Per-source counts for the sidebar badges (respecting only that source's own toggle). const countsBySource = useMemo(() => { const counts = Object.fromEntries(SOURCES.map((s) => [s.key, 0])) for (const r of feeds.store) counts[r.source] = (counts[r.source] || 0) + 1 return counts }, [feeds.store]) return ( <div className="flex h-screen flex-col overflow-hidden bg-[#0a0e14] text-slate-200"> <Header activeCount={feeds.activeCount} totalSources={feeds.totalSources} lastPull={feeds.lastPull} loading={feeds.loading} displayTick={feeds.displayTick} triageCount={feeds.triageCount} /> <div className="flex min-h-0 flex-1"> <Sidebar enabled={feeds.enabled} onToggle={feeds.toggleSource} refreshKey={feeds.refreshKey} onRefreshChange={feeds.setRefreshKey} cadenceKey={feeds.cadenceKey} onCadenceChange={feeds.setCadenceKey} onForceSync={() => feeds.pull()} loading={feeds.loading} visibleCountBySource={countsBySource} sourceStatus={feeds.sourceStatus} /> <main className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden p-4"> {showError && ( <div className="flex items-center gap-3 rounded-lg border border-rose-500/40 bg-rose-500/10 px-4 py-2.5 text-sm text-rose-200"> <AlertTriangle className="h-4 w-4 shrink-0 text-rose-400" /> <div className="min-w-0 flex-1"> <span className="font-medium">Ingestion service error:</span>{' '} <span className="text-rose-300/90">{feeds.error}</span> </div> <button onClick={() => setDismissedError(feeds.error)} className="shrink-0 rounded p-1 text-rose-300 hover:bg-rose-500/20" aria-label="Dismiss" > <X className="h-4 w-4" /> </button> </div> )} <MetricsRow activeCount={feeds.activeCount} totalSources={feeds.totalSources} lastPull={feeds.lastPull} totalIocs={feeds.visible.length} loading={feeds.loading} /> <CriticalTriagePanel rows={feeds.critical} loading={feeds.loading} onSelect={setSelected} displayTick={feeds.displayTick} acknowledgedIds={feeds.acknowledgedIds} onAcknowledge={feeds.acknowledge} onAcknowledgeAll={feeds.acknowledgeAll} /> <CTIFeed rows={feeds.visible} loading={feeds.loading} onSelect={setSelected} displayTick={feeds.displayTick} /> </main> </div> <DetailsDrawer row={selected} onClose={() => setSelected(null)} /> </div> ) } |