feat: forward decky.*.service.* on per-topology SSE stream
The /topologies/{id}/events SSE proxy now subscribes to two bus
patterns concurrently and merges them through a bounded asyncio.Queue:
* topology.{id}.> — lifecycle (status, mutation.*) — unchanged.
* decky.> — per-decky events, filtered by payload.topology_id
so a fleet decky sharing a name with a topology
decky doesn't leak across.
_sse_name_for routes 'decky.<name>.service.added' to the SSE event
name 'decky.service.added' (kept the prefix so the frontend doesn't
collide with topology lifecycle events that share leaf names like
'status').
useTopologyStream surfaces the two new event names; MazeNET.tsx's
onStreamEvent optimistically patches the matching node's services
list so a second tab reflects shape changes without a refetch.
This commit is contained in:
@@ -650,6 +650,22 @@ const MazeNET: React.FC = () => {
|
||||
|| event.name === 'status') {
|
||||
refetch();
|
||||
}
|
||||
// Live service mutations from another tab / admin: optimistically
|
||||
// patch local state so the chip set reflects shape without a full
|
||||
// re-hydrate. The post-mutation services list lives on the
|
||||
// payload; same shape the actor's POST/DELETE response carries.
|
||||
if (event.name === 'decky.service.added'
|
||||
|| event.name === 'decky.service.removed') {
|
||||
const p = event.payload ?? {};
|
||||
const deckyName = typeof p.decky_name === 'string' ? p.decky_name : null;
|
||||
const services = Array.isArray(p.services) ? p.services as string[] : null;
|
||||
if (deckyName && services) {
|
||||
setNodes((prev) => prev.map((n) => n.kind === 'decky' && n.name === deckyName
|
||||
? { ...n, services } : n));
|
||||
setStreamLive(true);
|
||||
setLastEventAt(new Date());
|
||||
}
|
||||
}
|
||||
}, [refetch]);
|
||||
const onStreamError = useCallback(() => { setStreamLive(false); }, []);
|
||||
useTopologyStream({
|
||||
|
||||
Reference in New Issue
Block a user