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.
This commit is contained in:
rcourtman 2025-12-07 23:52:00 +00:00
parent a10c13dfe9
commit cc4b32ad4a
4 changed files with 24 additions and 0 deletions

View file

@ -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

View file

@ -74,6 +74,8 @@ export function createWebSocketStore(url: string) {
activeAlerts: [],
recentlyResolved: [],
lastUpdate: '',
// Unified resources for cross-platform monitoring
resources: [],
});
const [activeAlerts, setActiveAlerts] = createStore<Record<string, Alert>>({});
const [recentlyResolved, setRecentlyResolved] = createStore<Record<string, ResolvedAlert>>({});
@ -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<string, Alert> = {};

View file

@ -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")
}
}
}

View file

@ -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
}