@@ -3,8 +3,12 @@ import type { ComponentChildren } from "preact";
33import { api } from "./api" ;
44import type { Status , TrackedIp , PrefixLoad , Mitigation , FwEvent , RuleSet , UpgradeStatus } from "./api" ;
55import { fmtn , fmtbps , timeStr , loadColor , dropColor } from "./format" ;
6- import { LoadBar , Sparkline , PagedTable , Section , flagCell } from "./ui" ;
7- import { Login , LimitsCard , MitigationsCard , SourcesCard , PortsCard , geoCell } from "./cards" ;
6+ import { LoadBar , Sparkline , PagedTable , Section , Modal , flagCell } from "./ui" ;
7+ import { Login , LimitsCard , MitigationsCard , SourcesCard , PortsCard , OriginsCard , geoCell } from "./cards" ;
8+ import {
9+ IconUpgrade , IconRefresh , IconSettings , IconUpdateCheck , IconDisconnect ,
10+ IconArrowDown , IconArrowUp , IconNoSymbol , IconShieldCheck , IconShieldAlert ,
11+ } from "./icons" ;
812
913interface Data {
1014 status : Status | null ;
@@ -30,6 +34,7 @@ const DEMO_TOKEN = (import.meta.env.VITE_DEMO_TOKEN as string | undefined) || ""
3034export function App ( ) {
3135 const [ token , setToken ] = useState ( localStorage . getItem ( "fwtoken" ) || DEMO_TOKEN ) ;
3236 const [ auto , setAuto ] = useState ( true ) ;
37+ const [ showLimits , setShowLimits ] = useState ( false ) ;
3338 const [ d , setD ] = useState < Data > ( EMPTY ) ;
3439 const [ upMsg , setUpMsg ] = useState ( "" ) ;
3540 const [ events , setEvents ] = useState < FwEvent [ ] > ( [ ] ) ;
@@ -118,11 +123,14 @@ export function App() {
118123 </ div >
119124 ) ;
120125 } ;
121- const meter = ( dir : string , lbl : string , color : string , big : string , sub : string , spark : ComponentChildren , sat : ComponentChildren ) => (
126+ const meter = ( dir : ComponentChildren , lbl : string , color : string , big : string , sub : string , spark : ComponentChildren , sat : ComponentChildren ) => (
122127 < div class = "meter" >
123- < span class = "dir" style = { { color } } > { dir } </ span >
124- < div class = "stack" > < span class = "lbl" > { lbl } </ span > < span class = "big" style = { { color } } > { big } </ span > < span class = "sub" > { sub } </ span > </ div >
125- { spark } { sat }
128+ < div class = "meter-main" >
129+ < span class = "dir" style = { { color } } > { dir } </ span >
130+ < div class = "stack" > < span class = "lbl" > { lbl } </ span > < span class = "big" style = { { color } } > { big } </ span > < span class = "sub" > { sub } </ span > </ div >
131+ { spark }
132+ </ div >
133+ { sat }
126134 </ div >
127135 ) ;
128136
@@ -143,8 +151,8 @@ export function App() {
143151 const evRows = events . map ( ( e ) => [ e . seq , timeStr ( e . ts_unix ) , e . kind , e . cidr , flagCell ( e . flags ) , fmtn ( e . pps ) , fmtbps ( e . bps ) , fmtn ( e . syn_pps ) ] ) ;
144152
145153 const posture = s . active_mitigations > 0
146- ? < span class = "chip atk" > ● under attack</ span >
147- : < span class = "chip arm" > ● armed</ span > ;
154+ ? < span class = "chip atk" > < IconShieldAlert /> under attack</ span >
155+ : < span class = "chip arm" > < IconShieldCheck /> armed</ span > ;
148156
149157 return (
150158 < >
@@ -154,36 +162,46 @@ export function App() {
154162 < span class = "muted" > up { s . uptime_secs } s · { s . active_mitigations } active · { s . learned_ports } ports{ nicStr ? " · " + nicStr : "" } </ span >
155163 { d . err ? < span class = "err" > { d . err } </ span > : null }
156164 { d . upgrade && d . upgrade . available
157- ? < button title = { "download & install " + d . upgrade . latest + ", then restart" } onClick = { doUpgrade } > ⬆ upgrade { d . upgrade . latest } </ button >
165+ ? < button title = { "download & install " + d . upgrade . latest + ", then restart" } onClick = { doUpgrade } > < IconUpgrade /> upgrade { d . upgrade . latest } </ button >
158166 : null }
159167 { upMsg ? < span class = "muted" > { upMsg } </ span > : null }
160168 < span class = "grow" />
161- < button class = "ghost" title = "Check for updates now" onClick = { checkUpdates } > ↻</ button >
169+ < button class = "ghost icon" title = "Detection limits" onClick = { ( ) => setShowLimits ( true ) } > < IconSettings /> </ button >
170+ < button class = "ghost icon" title = "Check for updates now" onClick = { checkUpdates } > < IconUpdateCheck /> </ button >
162171 < label class = "muted" > < input type = "checkbox" checked = { auto } onChange = { ( e ) => setAuto ( ( e . target as HTMLInputElement ) . checked ) } /> auto</ label >
163- < button class = "ghost" onClick = { refresh } > refresh </ button >
164- < button class = "ghost" onClick = { disconnect } > disconnect </ button >
172+ < button class = "ghost icon" title = "Refresh now" onClick = { refresh } > < IconRefresh /> </ button >
173+ < button class = "ghost icon" title = "Disconnect" onClick = { disconnect } > < IconDisconnect /> </ button >
165174 </ header >
166175 < main >
167176 { t0 && (
168- < section class = "wide" > < div class = "traffic" >
169- { meter ( "↓" , "rx · ingress" , "#2fd4c4" , fmtbps ( t0 . rx_bps ) , fmtn ( t0 . rx_pps ) + " pps · " + fmtn ( t0 . rx_syn_pps ) + " syn/s" ,
170- < Sparkline data = { hist . rx } color = "#2fd4c4" /> , satBar ( t0 . rx_bps ) ) }
171- { meter ( "↑" , "tx · egress" , "#9a86ff" , fmtbps ( t0 . tx_bps ) , fmtn ( t0 . tx_pps ) + " pps" ,
172- < Sparkline data = { hist . tx } color = "#9a86ff" /> , satBar ( t0 . tx_bps ) ) }
173- { meter ( "●" , "dropped" , dropColor ( t0 . rx_drop_pct ) , t0 . rx_drop_pct + "%" , fmtn ( t0 . rx_drop_pps ) + " pps" ,
174- < Sparkline data = { hist . drop } color = { dropColor ( t0 . rx_drop_pct ) } max = { 100 } /> , null ) }
175- </ div > </ section >
177+ < div class = "wide split2" >
178+ < div class = "split2-col" >
179+ < section > < div class = "traffic" >
180+ { meter ( < IconArrowDown /> , "rx · ingress" , "#2fd4c4" , fmtbps ( t0 . rx_bps ) , fmtn ( t0 . rx_pps ) + " pps · " + fmtn ( t0 . rx_syn_pps ) + " syn/s" ,
181+ < Sparkline data = { hist . rx } color = "#2fd4c4" /> , satBar ( t0 . rx_bps ) ) }
182+ { meter ( < IconArrowUp /> , "tx · egress" , "#9a86ff" , fmtbps ( t0 . tx_bps ) , fmtn ( t0 . tx_pps ) + " pps" ,
183+ < Sparkline data = { hist . tx } color = "#9a86ff" /> , satBar ( t0 . tx_bps ) ) }
184+ { meter ( < IconNoSymbol /> , "dropped" , dropColor ( t0 . rx_drop_pct ) , t0 . rx_drop_pct + "%" , fmtn ( t0 . rx_drop_pps ) + " pps" ,
185+ < Sparkline data = { hist . drop } color = { dropColor ( t0 . rx_drop_pct ) } max = { 100 } /> , null ) }
186+ </ div > </ section >
187+ < Section title = "Protected prefixes" extra = { "(" + d . prefixes . length + ")" } >
188+ < PagedTable cols = { [ "prefix" , "origin" , "rx pps" , "rx bps" , "tx pps" , "tx bps" , "syn/s" , "drop/s" , "drop%" , "load" , "state" ] } rows = { prefixRows } />
189+ </ Section >
190+ </ div >
191+ < Section title = "Traffic origins" extra = "source countries by pps" > < OriginsCard token = { token } /> </ Section >
192+ </ div >
193+ ) }
194+ { showLimits && (
195+ < Modal title = "Detection limits" onClose = { ( ) => setShowLimits ( false ) } >
196+ < LimitsCard token = { token } nics = { s . nics } />
197+ </ Modal >
176198 ) }
177- < Section wide title = "Detection limits" > < LimitsCard token = { token } nics = { s . nics } /> </ Section >
178199 < Section wide title = "Active mitigations" extra = { "(" + d . mitigations . length + ")" } >
179200 < MitigationsCard token = { token } mitigations = { d . mitigations } onChange = { refresh } />
180201 </ Section >
181202 < Section wide title = "Live tracked IPs" extra = { "(" + d . tracked . length + ")" } >
182203 < PagedTable cols = { [ "ip" , "origin" , "rx pps" , "rx bps" , "tx pps" , "tx bps" , "syn/s" , "drop/s" , "drop%" , "load" , "state" ] } rows = { trackedRows } />
183204 </ Section >
184- < Section wide title = "Protected prefixes" extra = { "(" + d . prefixes . length + ")" } >
185- < PagedTable cols = { [ "prefix" , "origin" , "rx pps" , "rx bps" , "tx pps" , "tx bps" , "syn/s" , "drop/s" , "drop%" , "load" , "state" ] } rows = { prefixRows } />
186- </ Section >
187205 < Section wide title = "Sources" > < SourcesCard token = { token } /> </ Section >
188206 < PortsCard token = { token } />
189207 < Section wide title = "Events" extra = { "(" + events . length + ")" } >
0 commit comments