fix: tag indicators now only show for guests that actually have tags

- Added ToFrontend() method to StateSnapshot for proper data conversion
- Modified /api/state endpoint to use frontend-formatted data
- Enhanced WebSocket store to handle tag data transformation consistently
- Ensures tags are properly converted between backend strings and frontend arrays
This commit is contained in:
Pulse Monitor 2025-08-31 18:01:47 +00:00
parent 426f4b274e
commit 21d784164a
3 changed files with 95 additions and 3 deletions

View file

@ -99,8 +99,58 @@ export function createWebSocketStore(url: string) {
console.log('[WebSocket] Updating nodes:', message.data.nodes?.length || 0);
setState('nodes', message.data.nodes);
}
if (message.data.vms !== undefined) setState('vms', message.data.vms);
if (message.data.containers !== undefined) setState('containers', message.data.containers);
if (message.data.vms !== undefined) {
// Transform tags from comma-separated strings to arrays
const transformedVMs = message.data.vms.map((vm: any) => {
const originalTags = vm.tags;
let transformedTags;
if (originalTags && typeof originalTags === 'string' && originalTags.trim()) {
// String with content - split into array
transformedTags = originalTags.split(',').map((t: string) => t.trim()).filter((t: string) => t.length > 0);
} else if (Array.isArray(originalTags)) {
// Already an array - filter out empty/whitespace-only tags
transformedTags = originalTags.filter((tag: any) =>
typeof tag === 'string' && tag.trim().length > 0
);
} else {
// null, undefined, empty string, or other - convert to empty array
transformedTags = [];
}
return {
...vm,
tags: transformedTags
};
});
setState('vms', transformedVMs);
}
if (message.data.containers !== undefined) {
// Transform tags from comma-separated strings to arrays
const transformedContainers = message.data.containers.map((container: any) => {
const originalTags = container.tags;
let transformedTags;
if (originalTags && typeof originalTags === 'string' && originalTags.trim()) {
// String with content - split into array
transformedTags = originalTags.split(',').map((t: string) => t.trim()).filter((t: string) => t.length > 0);
} else if (Array.isArray(originalTags)) {
// Already an array - filter out empty/whitespace-only tags
transformedTags = originalTags.filter((tag: any) =>
typeof tag === 'string' && tag.trim().length > 0
);
} else {
// null, undefined, empty string, or other - convert to empty array
transformedTags = [];
}
return {
...container,
tags: transformedTags
};
});
setState('containers', transformedContainers);
}
if (message.data.storage !== undefined) setState('storage', message.data.storage);
if (message.data.pbs !== undefined) setState('pbs', message.data.pbs);
if (message.data.pbsBackups !== undefined) setState('pbsBackups', message.data.pbsBackups);

View file

@ -1513,8 +1513,9 @@ func (r *Router) handleState(w http.ResponseWriter, req *http.Request) {
}
state := r.monitor.GetState()
frontendState := state.ToFrontend()
if err := utils.WriteJSONResponse(w, state); err != nil {
if err := utils.WriteJSONResponse(w, frontendState); err != nil {
log.Error().Err(err).Msg("Failed to encode state response")
writeErrorResponse(w, http.StatusInternalServerError, "encoding_error",
"Failed to encode state data", nil)

View file

@ -53,4 +53,45 @@ func (s *State) GetSnapshot() StateSnapshot {
}
return snapshot
}
// ToFrontend converts a StateSnapshot to frontend format with proper tag handling
func (s StateSnapshot) ToFrontend() StateFrontend {
// Convert nodes
nodes := make([]NodeFrontend, len(s.Nodes))
for i, n := range s.Nodes {
nodes[i] = n.ToFrontend()
}
// Convert VMs
vms := make([]VMFrontend, len(s.VMs))
for i, v := range s.VMs {
vms[i] = v.ToFrontend()
}
// Convert containers
containers := make([]ContainerFrontend, len(s.Containers))
for i, c := range s.Containers {
containers[i] = c.ToFrontend()
}
// Convert storage
storage := make([]StorageFrontend, len(s.Storage))
for i, st := range s.Storage {
storage[i] = st.ToFrontend()
}
return StateFrontend{
Nodes: nodes,
VMs: vms,
Containers: containers,
Storage: storage,
PBS: s.PBSInstances,
Metrics: make(map[string]any),
PVEBackups: s.PVEBackups,
Performance: make(map[string]any),
ConnectionHealth: s.ConnectionHealth,
Stats: make(map[string]any),
LastUpdate: s.LastUpdate.Unix() * 1000, // JavaScript timestamp
}
}