diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index 673e420..e15191a 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -93,6 +93,8 @@ type ResourceStoreInterface interface { GetPollingRecommendations() map[string]float64 // GetAll returns all resources in the store (for WebSocket broadcasts) GetAll() []resources.Resource + // PopulateFromSnapshot updates the store with data from a StateSnapshot + PopulateFromSnapshot(snapshot models.StateSnapshot) } func getNodeDisplayName(instance *config.PVEInstance, nodeName string) string { @@ -3290,7 +3292,8 @@ func (m *Monitor) Start(ctx context.Context, wsHub *websocket.Hub) { Msg("Broadcasting state update (ticker)") // Convert to frontend format before broadcasting (converts time.Time to int64, etc.) frontendState := state.ToFrontend() - // Inject unified resources if resource store is available + // Update and inject unified resources if resource store is available + m.updateResourceStore(state) frontendState.Resources = m.getResourcesForBroadcast() wsHub.BroadcastState(frontendState) @@ -6901,7 +6904,9 @@ func (m *Monitor) SetMockMode(enable bool) { m.mu.RUnlock() if hub != nil { - frontendState := m.GetState().ToFrontend() + state := m.GetState() + frontendState := state.ToFrontend() + m.updateResourceStore(state) frontendState.Resources = m.getResourcesForBroadcast() hub.BroadcastState(frontendState) } @@ -7057,6 +7062,20 @@ func (m *Monitor) shouldSkipNodeMetrics(nodeName string) bool { return should } +// updateResourceStore populates the resource store with data from the current state. +// This should be called before broadcasting to ensure fresh data. +func (m *Monitor) updateResourceStore(state models.StateSnapshot) { + m.mu.RLock() + store := m.resourceStore + m.mu.RUnlock() + + if store == nil { + return + } + + store.PopulateFromSnapshot(state) +} + // getResourcesForBroadcast retrieves all resources from the store and converts them to frontend format. // Returns nil if no resource store is configured. func (m *Monitor) getResourcesForBroadcast() []models.ResourceFrontend { diff --git a/internal/resources/store.go b/internal/resources/store.go index d357d87..26d322b 100644 --- a/internal/resources/store.go +++ b/internal/resources/store.go @@ -4,6 +4,8 @@ import ( "strings" "sync" "time" + + "github.com/rcourtman/pulse-go-rewrite/internal/models" ) // Store maintains a unified collection of all resources with deduplication. @@ -1024,3 +1026,54 @@ type PlatformSummary struct { Count int } +// PopulateFromSnapshot converts all resources from a StateSnapshot to the unified store. +// This should be called whenever the state is updated (e.g., before WebSocket broadcasts). +func (s *Store) PopulateFromSnapshot(snapshot models.StateSnapshot) { + // Convert nodes + for _, node := range snapshot.Nodes { + r := FromNode(node) + s.Upsert(r) + } + + // Convert VMs + for _, vm := range snapshot.VMs { + r := FromVM(vm) + s.Upsert(r) + } + + // Convert containers + for _, ct := range snapshot.Containers { + r := FromContainer(ct) + s.Upsert(r) + } + + // Convert hosts + for _, host := range snapshot.Hosts { + r := FromHost(host) + s.Upsert(r) + } + + // Convert docker hosts and their containers + for _, dh := range snapshot.DockerHosts { + r := FromDockerHost(dh) + s.Upsert(r) + + // Convert containers within the docker host + for _, dc := range dh.Containers { + r := FromDockerContainer(dc, dh.ID, dh.Hostname) + s.Upsert(r) + } + } + + // Convert PBS instances + for _, pbs := range snapshot.PBSInstances { + r := FromPBSInstance(pbs) + s.Upsert(r) + } + + // Convert storage + for _, storage := range snapshot.Storage { + r := FromStorage(storage) + s.Upsert(r) + } +}