From cc4b32ad4aac0fc68a26265cf2929bc37916ced1 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 7 Dec 2025 23:52:00 +0000 Subject: [PATCH] fix: complete unified resources WebSocket integration Backend: - Call SetMonitor after router creation to inject resource store - Add debug logging for resource population and broadcast Frontend: - Add resources array to WebSocket store initial state - Handle resources in WebSocket message processing - Use reconcile for efficient state updates The unified resources are now properly: 1. Populated from StateSnapshot on each broadcast cycle 2. Converted to frontend format (ResourceFrontend) 3. Included in WebSocket state messages 4. Received and stored in frontend state 5. Consumed by migrated route components Console now shows '[DashboardView] Using unified resources: VMs: X' confirming the migration is working end-to-end. --- cmd/pulse/main.go | 4 ++++ frontend-modern/src/stores/websocket.ts | 6 ++++++ internal/api/router.go | 3 +++ internal/monitoring/monitor.go | 11 +++++++++++ 4 files changed, 24 insertions(+) diff --git a/cmd/pulse/main.go b/cmd/pulse/main.go index c0d0037..a299ac9 100644 --- a/cmd/pulse/main.go +++ b/cmd/pulse/main.go @@ -172,6 +172,10 @@ func runServer() { return nil } router = api.NewRouter(cfg, reloadableMonitor.GetMonitor(), wsHub, reloadFunc, Version) + + // Inject resource store into monitor for WebSocket broadcasts + // This must be done after router creation since resourceHandlers is created in NewRouter + router.SetMonitor(reloadableMonitor.GetMonitor()) // Create HTTP server with unified configuration // In production, serve everything (frontend + API) on the frontend port diff --git a/frontend-modern/src/stores/websocket.ts b/frontend-modern/src/stores/websocket.ts index 78c4443..e1fd52a 100644 --- a/frontend-modern/src/stores/websocket.ts +++ b/frontend-modern/src/stores/websocket.ts @@ -74,6 +74,8 @@ export function createWebSocketStore(url: string) { activeAlerts: [], recentlyResolved: [], lastUpdate: '', + // Unified resources for cross-platform monitoring + resources: [], }); const [activeAlerts, setActiveAlerts] = createStore>({}); const [recentlyResolved, setRecentlyResolved] = createStore>({}); @@ -580,6 +582,10 @@ export function createWebSocketStore(url: string) { if (message.data.stats !== undefined) setState('stats', message.data.stats); if (message.data.physicalDisks !== undefined) setState('physicalDisks', reconcile(message.data.physicalDisks, { key: 'id' })); + // Handle unified resources + if (message.data.resources !== undefined) { + setState('resources', reconcile(message.data.resources, { key: 'id' })); + } // Sync active alerts from state if (message.data.activeAlerts !== undefined) { const newAlerts: Record = {}; diff --git a/internal/api/router.go b/internal/api/router.go index cf0e6fb..c96bfac 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -1278,9 +1278,12 @@ func (r *Router) SetMonitor(m *monitoring.Monitor) { } // Inject resource store for polling optimization if r.resourceHandlers != nil { + log.Debug().Msg("[Router] Injecting resource store into monitor") m.SetResourceStore(r.resourceHandlers.Store()) // Also set state provider for on-demand resource population r.resourceHandlers.SetStateProvider(m) + } else { + log.Warn().Msg("[Router] resourceHandlers is nil, cannot inject resource store") } } } diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index e15191a..3ce4678 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -7070,9 +7070,18 @@ func (m *Monitor) updateResourceStore(state models.StateSnapshot) { m.mu.RUnlock() if store == nil { + log.Debug().Msg("[Resources] No resource store configured, skipping population") return } + log.Debug(). + Int("nodes", len(state.Nodes)). + Int("vms", len(state.VMs)). + Int("containers", len(state.Containers)). + Int("hosts", len(state.Hosts)). + Int("dockerHosts", len(state.DockerHosts)). + Msg("[Resources] Populating resource store from state snapshot") + store.PopulateFromSnapshot(state) } @@ -7084,10 +7093,12 @@ func (m *Monitor) getResourcesForBroadcast() []models.ResourceFrontend { m.mu.RUnlock() if store == nil { + log.Debug().Msg("[Resources] No store for broadcast") return nil } allResources := store.GetAll() + log.Debug().Int("count", len(allResources)).Msg("[Resources] Got resources for broadcast") if len(allResources) == 0 { return nil }