feat(web): show MazeNET decky running count + roll into dashboard

MazeNET header now reports '{running}/{total} DECKIES RUNNING' so
operators can see per-topology runtime status at a glance.

Dashboard ACTIVE DECKIES counters used to reflect only the fleet state
file; TopologyDecky rows (MazeNET deployments) are now added in —
deployed_deckies = fleet + all topology rows, active_deckies = fleet
(no runtime field) + topology rows whose state is 'running'.
This commit is contained in:
2026-04-22 17:48:04 -04:00
parent b802d59c70
commit 3f460bab84
2 changed files with 19 additions and 4 deletions

View File

@@ -349,15 +349,27 @@ class SQLModelRepository(BaseRepository):
select(func.count(func.distinct(Log.attacker_ip)))
)
).scalar() or 0
topo_total = (
await session.execute(select(func.count()).select_from(TopologyDecky))
).scalar() or 0
topo_running = (
await session.execute(
select(func.count())
.select_from(TopologyDecky)
.where(TopologyDecky.state == "running")
)
).scalar() or 0
_state = await asyncio.to_thread(load_state)
deployed_deckies = len(_state[0].deckies) if _state else 0
fleet_deckies = len(_state[0].deckies) if _state else 0
return {
"total_logs": total_logs,
"unique_attackers": unique_attackers,
"active_deckies": deployed_deckies,
"deployed_deckies": deployed_deckies,
# Fleet state file doesn't track per-decky runtime; treat all
# fleet rows as active and add MazeNET running rows on top.
"active_deckies": fleet_deckies + topo_running,
"deployed_deckies": fleet_deckies + topo_total,
}
async def get_deckies(self) -> List[dict]:

View File

@@ -529,6 +529,8 @@ const MazeNET: React.FC = () => {
}, []);
const canDeploy = topoStatus === 'pending' && nets.length > 0;
const deckyNodes = nodes.filter((n) => n.kind === 'decky');
const runningDeckies = deckyNodes.filter((n) => n.status === 'active').length;
return (
<div className="maze-page">
@@ -537,7 +539,8 @@ const MazeNET: React.FC = () => {
<h1>MAZENET · {topoName || topologyId}</h1>
<div className="maze-page-sub">
NETWORK OF NETWORKS · {topoStatus.toUpperCase()} · v{topoVersion} ·{' '}
{nets.length} NETS · {nodes.length} NODES · {edges.length} PATHS
{nets.length} NETS · {nodes.length} NODES · {edges.length} PATHS ·{' '}
{runningDeckies}/{deckyNodes.length} DECKIES RUNNING
{streamEnabled && (
<span className="alert-text" style={{ color: streamLive ? undefined : 'var(--fg-dim)' }}>
{' '}· {streamLive ? 'LIVE' : 'CONNECTING…'}