package models import "encoding/json" // Frontend-friendly type aliases with proper JSON tags // These extend the base types with additional computed fields // NodeFrontend represents a Node with frontend-friendly field names type NodeFrontend struct { ID string `json:"id"` Node string `json:"node"` // Maps to Name Name string `json:"name"` DisplayName string `json:"displayName"` Instance string `json:"instance"` Host string `json:"host,omitempty"` GuestURL string `json:"guestURL,omitempty"` // Optional guest-accessible URL (for navigation) Status string `json:"status"` Type string `json:"type"` CPU float64 `json:"cpu"` Memory *Memory `json:"memory,omitempty"` // Full memory object with usage percentage Mem int64 `json:"mem"` // Maps to Memory.Used (kept for backward compat) MaxMem int64 `json:"maxmem"` // Maps to Memory.Total (kept for backward compat) Disk *Disk `json:"disk,omitempty"` // Full disk object with usage percentage MaxDisk int64 `json:"maxdisk"` // Maps to Disk.Total (kept for backward compat) Uptime int64 `json:"uptime"` LoadAverage []float64 `json:"loadAverage"` KernelVersion string `json:"kernelVersion"` PVEVersion string `json:"pveVersion"` CPUInfo CPUInfo `json:"cpuInfo"` Temperature *Temperature `json:"temperature,omitempty"` // CPU/NVMe temperatures LastSeen int64 `json:"lastSeen"` // Unix timestamp ConnectionHealth string `json:"connectionHealth"` IsClusterMember bool `json:"isClusterMember,omitempty"` ClusterName string `json:"clusterName,omitempty"` TemperatureMonitoringEnabled *bool `json:"temperatureMonitoringEnabled,omitempty"` // Per-node temperature monitoring override LinkedAgentID string `json:"linkedAgentId,omitempty"` // ID of linked agent (for "Via agent" badge) } func (n NodeFrontend) NormalizeCollections() NodeFrontend { if n.LoadAverage == nil { n.LoadAverage = []float64{} } return n } // VMFrontend represents a VM with frontend-friendly field names type VMFrontend struct { ID string `json:"id"` VMID int `json:"vmid"` Name string `json:"name"` Node string `json:"node"` Pool string `json:"pool,omitempty"` Instance string `json:"instance"` Status string `json:"status"` Type string `json:"type"` CPU float64 `json:"cpu"` CPUs int `json:"cpus"` Memory *Memory `json:"memory,omitempty"` // Full memory object Mem int64 `json:"mem"` // Maps to Memory.Used MaxMem int64 `json:"maxmem"` // Maps to Memory.Total DiskObj *Disk `json:"disk,omitempty"` // Full disk object Disks []Disk `json:"disks"` // Individual filesystem/disk usage DiskStatusReason string `json:"diskStatusReason,omitempty"` // Why disk stats are unavailable OSName string `json:"osName,omitempty"` OSVersion string `json:"osVersion,omitempty"` AgentVersion string `json:"agentVersion,omitempty"` NetworkInterfaces []GuestNetworkInterface `json:"networkInterfaces"` IPAddresses []string `json:"ipAddresses"` NetIn int64 `json:"networkIn"` // Maps to NetworkIn (camelCase for frontend) NetOut int64 `json:"networkOut"` // Maps to NetworkOut (camelCase for frontend) DiskRead int64 `json:"diskRead"` // Maps to DiskRead (camelCase for frontend) DiskWrite int64 `json:"diskWrite"` // Maps to DiskWrite (camelCase for frontend) Uptime int64 `json:"uptime"` Template bool `json:"template"` LastBackup int64 `json:"lastBackup,omitempty"` // Unix timestamp Tags string `json:"tags,omitempty"` // Joined string Lock string `json:"lock,omitempty"` LastSeen int64 `json:"lastSeen"` // Unix timestamp } func (v VMFrontend) NormalizeCollections() VMFrontend { if v.Disks == nil { v.Disks = []Disk{} } if v.NetworkInterfaces == nil { v.NetworkInterfaces = []GuestNetworkInterface{} } if v.IPAddresses == nil { v.IPAddresses = []string{} } return v } // ContainerFrontend represents a Container with frontend-friendly field names type ContainerFrontend struct { ID string `json:"id"` VMID int `json:"vmid"` Name string `json:"name"` Node string `json:"node"` Pool string `json:"pool,omitempty"` Instance string `json:"instance"` Status string `json:"status"` Type string `json:"type"` // OCI container support (Proxmox VE 9.1+) IsOCI bool `json:"isOci,omitempty"` // True if this is an OCI container OSTemplate string `json:"osTemplate,omitempty"` // Template or OCI image used (e.g., "oci:docker.io/library/alpine:latest") CPU float64 `json:"cpu"` CPUs int `json:"cpus"` Memory *Memory `json:"memory,omitempty"` // Full memory object Mem int64 `json:"mem"` // Maps to Memory.Used MaxMem int64 `json:"maxmem"` // Maps to Memory.Total DiskObj *Disk `json:"disk,omitempty"` // Full disk object Disks []Disk `json:"disks"` // Individual filesystem/disk usage NetworkInterfaces []GuestNetworkInterface `json:"networkInterfaces"` IPAddresses []string `json:"ipAddresses"` NetIn int64 `json:"networkIn"` // Maps to NetworkIn (camelCase for frontend) NetOut int64 `json:"networkOut"` // Maps to NetworkOut (camelCase for frontend) DiskRead int64 `json:"diskRead"` // Maps to DiskRead (camelCase for frontend) DiskWrite int64 `json:"diskWrite"` // Maps to DiskWrite (camelCase for frontend) Uptime int64 `json:"uptime"` Template bool `json:"template"` LastBackup int64 `json:"lastBackup,omitempty"` // Unix timestamp Tags string `json:"tags,omitempty"` // Joined string Lock string `json:"lock,omitempty"` LastSeen int64 `json:"lastSeen"` // Unix timestamp OSName string `json:"osName,omitempty"` } func (c ContainerFrontend) NormalizeCollections() ContainerFrontend { if c.Disks == nil { c.Disks = []Disk{} } if c.NetworkInterfaces == nil { c.NetworkInterfaces = []GuestNetworkInterface{} } if c.IPAddresses == nil { c.IPAddresses = []string{} } return c } // DockerHostFrontend represents a Docker host with frontend-friendly fields type DockerHostFrontend struct { ID string `json:"id"` AgentID string `json:"agentId"` Hostname string `json:"hostname"` DisplayName string `json:"displayName"` CustomDisplayName string `json:"customDisplayName,omitempty"` MachineID string `json:"machineId,omitempty"` OS string `json:"os,omitempty"` KernelVersion string `json:"kernelVersion,omitempty"` Architecture string `json:"architecture,omitempty"` Runtime string `json:"runtime"` RuntimeVersion string `json:"runtimeVersion,omitempty"` DockerVersion string `json:"dockerVersion,omitempty"` CPUs int `json:"cpus"` TotalMemoryBytes int64 `json:"totalMemoryBytes"` UptimeSeconds int64 `json:"uptimeSeconds"` CPUUsagePercent float64 `json:"cpuUsagePercent"` LoadAverage []float64 `json:"loadAverage"` Memory *Memory `json:"memory,omitempty"` Disks []Disk `json:"disks"` NetworkInterfaces []HostNetworkInterface `json:"networkInterfaces"` Status string `json:"status"` LastSeen int64 `json:"lastSeen"` IntervalSeconds int `json:"intervalSeconds"` AgentVersion string `json:"agentVersion,omitempty"` Containers []DockerContainerFrontend `json:"containers"` Services []DockerServiceFrontend `json:"services"` Tasks []DockerTaskFrontend `json:"tasks"` Nodes []DockerNodeFrontend `json:"nodes"` Secrets []DockerSecretFrontend `json:"secrets"` Configs []DockerConfigFrontend `json:"configs"` Swarm *DockerSwarmFrontend `json:"swarm,omitempty"` Security *DockerHostSecurityFrontend `json:"security,omitempty"` TokenID string `json:"tokenId,omitempty"` TokenName string `json:"tokenName,omitempty"` TokenHint string `json:"tokenHint,omitempty"` TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"` PendingUninstall bool `json:"pendingUninstall"` Command *DockerHostCommandFrontend `json:"command,omitempty"` } func (h DockerHostFrontend) NormalizeCollections() DockerHostFrontend { if h.LoadAverage == nil { h.LoadAverage = []float64{} } if h.Disks == nil { h.Disks = []Disk{} } if h.NetworkInterfaces == nil { h.NetworkInterfaces = []HostNetworkInterface{} } if h.Containers == nil { h.Containers = []DockerContainerFrontend{} } if h.Services == nil { h.Services = []DockerServiceFrontend{} } if h.Tasks == nil { h.Tasks = []DockerTaskFrontend{} } if h.Nodes == nil { h.Nodes = []DockerNodeFrontend{} } if h.Secrets == nil { h.Secrets = []DockerSecretFrontend{} } if h.Configs == nil { h.Configs = []DockerConfigFrontend{} } for i := range h.Containers { h.Containers[i] = h.Containers[i].NormalizeCollections() } for i := range h.Services { h.Services[i] = h.Services[i].NormalizeCollections() } for i := range h.Secrets { h.Secrets[i] = h.Secrets[i].NormalizeCollections() } for i := range h.Configs { h.Configs[i] = h.Configs[i].NormalizeCollections() } if h.Security != nil { security := h.Security.NormalizeCollections() h.Security = &security } return h } // DockerHostSecurityFrontend exposes docker-host security posture to the UI. type DockerHostSecurityFrontend struct { AuthorizationPlugins []string `json:"authorizationPlugins"` MutatingCommandsBlocked bool `json:"mutatingCommandsBlocked,omitempty"` MutatingCommandsBlockedReason string `json:"mutatingCommandsBlockedReason,omitempty"` } func (s DockerHostSecurityFrontend) NormalizeCollections() DockerHostSecurityFrontend { if s.AuthorizationPlugins == nil { s.AuthorizationPlugins = []string{} } return s } // ConnectedInfrastructureSurfaceFrontend describes one reporting surface that // Pulse associates with an infrastructure item in the connected-infrastructure // settings view. type ConnectedInfrastructureSurfaceFrontend struct { ID string `json:"id"` Kind string `json:"kind"` Label string `json:"label"` Detail string `json:"detail,omitempty"` ControlID string `json:"controlId,omitempty"` Action string `json:"action,omitempty"` IDLabel string `json:"idLabel,omitempty"` IDValue string `json:"idValue,omitempty"` } // ConnectedInfrastructureItemFrontend is the canonical Connected // infrastructure projection for the settings surface. It is derived from the // unified resource model plus reporting-ignore state, so the frontend does not // need to reconstruct machine rows from raw resource facets. type ConnectedInfrastructureItemFrontend struct { ID string `json:"id"` Name string `json:"name"` DisplayName string `json:"displayName,omitempty"` Hostname string `json:"hostname,omitempty"` Status string `json:"status"` HealthStatus string `json:"healthStatus,omitempty"` LastSeen int64 `json:"lastSeen,omitempty"` RemovedAt int64 `json:"removedAt,omitempty"` Version string `json:"version,omitempty"` IsOutdatedBinary bool `json:"isOutdatedBinary,omitempty"` LinkedNodeID string `json:"linkedNodeId,omitempty"` LinkedVMID string `json:"linkedVmId,omitempty"` LinkedContainerID string `json:"linkedContainerId,omitempty"` CommandsEnabled bool `json:"commandsEnabled,omitempty"` ScopeAgentID string `json:"scopeAgentId,omitempty"` UpgradePlatform string `json:"upgradePlatform,omitempty"` UninstallAgentID string `json:"uninstallAgentId,omitempty"` UninstallHostname string `json:"uninstallHostname,omitempty"` Surfaces []ConnectedInfrastructureSurfaceFrontend `json:"surfaces"` } func (i ConnectedInfrastructureItemFrontend) NormalizeCollections() ConnectedInfrastructureItemFrontend { if i.Surfaces == nil { i.Surfaces = []ConnectedInfrastructureSurfaceFrontend{} } return i } // KubernetesClusterFrontend represents a Kubernetes cluster for the frontend. type KubernetesClusterFrontend struct { ID string `json:"id"` AgentID string `json:"agentId"` Name string `json:"name,omitempty"` DisplayName string `json:"displayName,omitempty"` CustomDisplayName string `json:"customDisplayName,omitempty"` Server string `json:"server,omitempty"` Context string `json:"context,omitempty"` Version string `json:"version,omitempty"` Status string `json:"status"` LastSeen int64 `json:"lastSeen"` IntervalSeconds int `json:"intervalSeconds"` AgentVersion string `json:"agentVersion,omitempty"` TokenID string `json:"tokenId,omitempty"` TokenName string `json:"tokenName,omitempty"` TokenHint string `json:"tokenHint,omitempty"` TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"` Hidden bool `json:"hidden"` PendingUninstall bool `json:"pendingUninstall"` Nodes []KubernetesNodeFrontend `json:"nodes"` Pods []KubernetesPodFrontend `json:"pods"` Deployments []KubernetesDeploymentFrontend `json:"deployments"` } func (c KubernetesClusterFrontend) NormalizeCollections() KubernetesClusterFrontend { if c.Nodes == nil { c.Nodes = []KubernetesNodeFrontend{} } if c.Pods == nil { c.Pods = []KubernetesPodFrontend{} } if c.Deployments == nil { c.Deployments = []KubernetesDeploymentFrontend{} } for i := range c.Nodes { c.Nodes[i] = c.Nodes[i].NormalizeCollections() } for i := range c.Pods { c.Pods[i] = c.Pods[i].NormalizeCollections() } for i := range c.Deployments { c.Deployments[i] = c.Deployments[i].NormalizeCollections() } return c } type KubernetesNodeFrontend struct { UID string `json:"uid"` Name string `json:"name"` Ready bool `json:"ready"` Unschedulable bool `json:"unschedulable,omitempty"` KubeletVersion string `json:"kubeletVersion,omitempty"` ContainerRuntimeVersion string `json:"containerRuntimeVersion,omitempty"` OSImage string `json:"osImage,omitempty"` KernelVersion string `json:"kernelVersion,omitempty"` Architecture string `json:"architecture,omitempty"` CapacityCPU int64 `json:"capacityCpuCores,omitempty"` CapacityMemoryBytes int64 `json:"capacityMemoryBytes,omitempty"` CapacityPods int64 `json:"capacityPods,omitempty"` AllocCPU int64 `json:"allocatableCpuCores,omitempty"` AllocMemoryBytes int64 `json:"allocatableMemoryBytes,omitempty"` AllocPods int64 `json:"allocatablePods,omitempty"` UsageCPUMilliCores int64 `json:"usageCpuMilliCores,omitempty"` UsageMemoryBytes int64 `json:"usageMemoryBytes,omitempty"` UsageCPUPercent float64 `json:"usageCpuPercent,omitempty"` UsageMemoryPercent float64 `json:"usageMemoryPercent,omitempty"` Roles []string `json:"roles"` } func (n KubernetesNodeFrontend) NormalizeCollections() KubernetesNodeFrontend { if n.Roles == nil { n.Roles = []string{} } return n } type KubernetesPodFrontend struct { UID string `json:"uid"` Name string `json:"name"` Namespace string `json:"namespace"` NodeName string `json:"nodeName,omitempty"` Phase string `json:"phase,omitempty"` Reason string `json:"reason,omitempty"` Message string `json:"message,omitempty"` QoSClass string `json:"qosClass,omitempty"` CreatedAt int64 `json:"createdAt,omitempty"` StartTime *int64 `json:"startTime,omitempty"` Restarts int `json:"restarts,omitempty"` UsageCPUMilliCores int `json:"usageCpuMilliCores,omitempty"` UsageMemoryBytes int64 `json:"usageMemoryBytes,omitempty"` UsageCPUPercent float64 `json:"usageCpuPercent,omitempty"` UsageMemoryPercent float64 `json:"usageMemoryPercent,omitempty"` NetworkRxBytes int64 `json:"networkRxBytes,omitempty"` NetworkTxBytes int64 `json:"networkTxBytes,omitempty"` NetInRate float64 `json:"netInRate,omitempty"` NetOutRate float64 `json:"netOutRate,omitempty"` EphemeralStorageUsedBytes int64 `json:"ephemeralStorageUsedBytes,omitempty"` EphemeralStorageCapacityBytes int64 `json:"ephemeralStorageCapacityBytes,omitempty"` DiskUsagePercent float64 `json:"diskUsagePercent,omitempty"` Labels map[string]string `json:"labels"` OwnerKind string `json:"ownerKind,omitempty"` OwnerName string `json:"ownerName,omitempty"` Containers []KubernetesPodContainerFrontend `json:"containers"` } func (p KubernetesPodFrontend) NormalizeCollections() KubernetesPodFrontend { if p.Labels == nil { p.Labels = map[string]string{} } if p.Containers == nil { p.Containers = []KubernetesPodContainerFrontend{} } return p } type KubernetesPodContainerFrontend struct { Name string `json:"name"` Image string `json:"image,omitempty"` Ready bool `json:"ready"` RestartCount int32 `json:"restartCount,omitempty"` State string `json:"state,omitempty"` Reason string `json:"reason,omitempty"` Message string `json:"message,omitempty"` } type KubernetesDeploymentFrontend struct { UID string `json:"uid"` Name string `json:"name"` Namespace string `json:"namespace"` CreatedAt int64 `json:"createdAt,omitempty"` DesiredReplicas int32 `json:"desiredReplicas,omitempty"` UpdatedReplicas int32 `json:"updatedReplicas,omitempty"` ReadyReplicas int32 `json:"readyReplicas,omitempty"` AvailableReplicas int32 `json:"availableReplicas,omitempty"` ObservedGeneration int64 `json:"observedGeneration,omitempty"` Labels map[string]string `json:"labels"` } func (d KubernetesDeploymentFrontend) NormalizeCollections() KubernetesDeploymentFrontend { if d.Labels == nil { d.Labels = map[string]string{} } return d } // DockerContainerFrontend represents a Docker container for the frontend type DockerContainerFrontend struct { ID string `json:"id"` Name string `json:"name"` Image string `json:"image"` State string `json:"state"` Status string `json:"status"` Health string `json:"health,omitempty"` CPUPercent float64 `json:"cpuPercent"` MemoryUsage int64 `json:"memoryUsageBytes"` MemoryLimit int64 `json:"memoryLimitBytes"` MemoryPercent float64 `json:"memoryPercent"` UptimeSeconds int64 `json:"uptimeSeconds"` RestartCount int `json:"restartCount"` ExitCode int `json:"exitCode"` CreatedAt int64 `json:"createdAt"` StartedAt *int64 `json:"startedAt,omitempty"` FinishedAt *int64 `json:"finishedAt,omitempty"` Ports []DockerContainerPortFrontend `json:"ports"` Labels map[string]string `json:"labels"` Networks []DockerContainerNetworkFrontend `json:"networks"` WritableLayerBytes int64 `json:"writableLayerBytes,omitempty"` RootFilesystemBytes int64 `json:"rootFilesystemBytes,omitempty"` BlockIO *DockerContainerBlockIOFrontend `json:"blockIo,omitempty"` Mounts []DockerContainerMountFrontend `json:"mounts"` Podman *DockerPodmanContainerFrontend `json:"podman,omitempty"` UpdateStatus *DockerContainerUpdateStatusFrontend `json:"updateStatus,omitempty"` } func (c DockerContainerFrontend) NormalizeCollections() DockerContainerFrontend { if c.Ports == nil { c.Ports = []DockerContainerPortFrontend{} } if c.Labels == nil { c.Labels = map[string]string{} } if c.Networks == nil { c.Networks = []DockerContainerNetworkFrontend{} } if c.Mounts == nil { c.Mounts = []DockerContainerMountFrontend{} } return c } // DockerContainerUpdateStatusFrontend tracks the image update status for a container. type DockerContainerUpdateStatusFrontend struct { UpdateAvailable bool `json:"updateAvailable"` CurrentDigest string `json:"currentDigest,omitempty"` LatestDigest string `json:"latestDigest,omitempty"` LastChecked int64 `json:"lastChecked"` Error string `json:"error,omitempty"` // e.g., "rate limited", "auth required" } // DockerContainerPortFrontend represents a container port mapping type DockerContainerPortFrontend struct { PrivatePort int `json:"privatePort"` PublicPort int `json:"publicPort,omitempty"` Protocol string `json:"protocol"` IP string `json:"ip,omitempty"` } // DockerContainerNetworkFrontend represents container network addresses type DockerContainerNetworkFrontend struct { Name string `json:"name"` IPv4 string `json:"ipv4,omitempty"` IPv6 string `json:"ipv6,omitempty"` } // DockerContainerBlockIOFrontend exposes aggregate block IO counters. type DockerContainerBlockIOFrontend struct { ReadBytes uint64 `json:"readBytes,omitempty"` WriteBytes uint64 `json:"writeBytes,omitempty"` ReadRateBytesPerSecond *float64 `json:"readRateBytesPerSecond,omitempty"` WriteRateBytesPerSecond *float64 `json:"writeRateBytesPerSecond,omitempty"` } // DockerContainerMountFrontend represents a container mount for the UI. type DockerContainerMountFrontend struct { Type string `json:"type,omitempty"` Source string `json:"source,omitempty"` Destination string `json:"destination,omitempty"` Mode string `json:"mode,omitempty"` RW bool `json:"rw"` Propagation string `json:"propagation,omitempty"` Name string `json:"name,omitempty"` Driver string `json:"driver,omitempty"` } // DockerPodmanContainerFrontend exposes podman-specific metadata. type DockerPodmanContainerFrontend struct { PodName string `json:"podName,omitempty"` PodID string `json:"podId,omitempty"` Infra bool `json:"infra,omitempty"` ComposeProject string `json:"composeProject,omitempty"` ComposeService string `json:"composeService,omitempty"` ComposeWorkdir string `json:"composeWorkdir,omitempty"` ComposeConfigHash string `json:"composeConfigHash,omitempty"` AutoUpdatePolicy string `json:"autoUpdatePolicy,omitempty"` AutoUpdateRestart string `json:"autoUpdateRestart,omitempty"` UserNamespace string `json:"userNamespace,omitempty"` } // DockerServiceFrontend represents a Swarm service for the frontend. type DockerServiceFrontend struct { ID string `json:"id"` Name string `json:"name"` Stack string `json:"stack,omitempty"` Image string `json:"image,omitempty"` Mode string `json:"mode,omitempty"` DesiredTasks int `json:"desiredTasks,omitempty"` RunningTasks int `json:"runningTasks,omitempty"` CompletedTasks int `json:"completedTasks,omitempty"` Labels map[string]string `json:"labels"` EndpointPorts []DockerServicePortFrontend `json:"endpointPorts"` UpdateStatus *DockerServiceUpdateFrontend `json:"updateStatus,omitempty"` CreatedAt *int64 `json:"createdAt,omitempty"` UpdatedAt *int64 `json:"updatedAt,omitempty"` } func (s DockerServiceFrontend) NormalizeCollections() DockerServiceFrontend { if s.Labels == nil { s.Labels = map[string]string{} } if s.EndpointPorts == nil { s.EndpointPorts = []DockerServicePortFrontend{} } return s } // DockerServicePortFrontend represents a published service port. type DockerServicePortFrontend struct { Name string `json:"name,omitempty"` Protocol string `json:"protocol,omitempty"` TargetPort uint32 `json:"targetPort,omitempty"` PublishedPort uint32 `json:"publishedPort,omitempty"` PublishMode string `json:"publishMode,omitempty"` } // DockerServiceUpdateFrontend exposes service update status to the UI. type DockerServiceUpdateFrontend struct { State string `json:"state,omitempty"` Message string `json:"message,omitempty"` CompletedAt *int64 `json:"completedAt,omitempty"` } // DockerTaskFrontend represents a Swarm task replica. type DockerTaskFrontend struct { ID string `json:"id"` ServiceID string `json:"serviceId,omitempty"` ServiceName string `json:"serviceName,omitempty"` Slot int `json:"slot,omitempty"` NodeID string `json:"nodeId,omitempty"` NodeName string `json:"nodeName,omitempty"` DesiredState string `json:"desiredState,omitempty"` CurrentState string `json:"currentState,omitempty"` Error string `json:"error,omitempty"` Message string `json:"message,omitempty"` ContainerID string `json:"containerId,omitempty"` ContainerName string `json:"containerName,omitempty"` CreatedAt *int64 `json:"createdAt,omitempty"` UpdatedAt *int64 `json:"updatedAt,omitempty"` StartedAt *int64 `json:"startedAt,omitempty"` CompletedAt *int64 `json:"completedAt,omitempty"` } // DockerNodeFrontend represents a Swarm node. type DockerNodeFrontend struct { ID string `json:"id"` Hostname string `json:"hostname,omitempty"` Role string `json:"role,omitempty"` Availability string `json:"availability,omitempty"` State string `json:"state,omitempty"` Message string `json:"message,omitempty"` Address string `json:"address,omitempty"` ManagerReachability string `json:"managerReachability,omitempty"` ManagerAddress string `json:"managerAddress,omitempty"` Leader bool `json:"leader,omitempty"` EngineVersion string `json:"engineVersion,omitempty"` OS string `json:"os,omitempty"` Architecture string `json:"architecture,omitempty"` NanoCPUs int64 `json:"nanoCpus,omitempty"` MemoryBytes int64 `json:"memoryBytes,omitempty"` Labels map[string]string `json:"labels,omitempty"` EngineLabels map[string]string `json:"engineLabels,omitempty"` CreatedAt *int64 `json:"createdAt,omitempty"` UpdatedAt *int64 `json:"updatedAt,omitempty"` } // DockerSecretFrontend represents Docker Swarm secret metadata for the frontend. type DockerSecretFrontend struct { ID string `json:"id"` Name string `json:"name"` Labels map[string]string `json:"labels"` DriverName string `json:"driverName,omitempty"` TemplatingDriver string `json:"templatingDriver,omitempty"` CreatedAt *int64 `json:"createdAt,omitempty"` UpdatedAt *int64 `json:"updatedAt,omitempty"` } func (s DockerSecretFrontend) NormalizeCollections() DockerSecretFrontend { if s.Labels == nil { s.Labels = map[string]string{} } return s } // DockerConfigFrontend represents Docker Swarm config metadata for the frontend. type DockerConfigFrontend struct { ID string `json:"id"` Name string `json:"name"` Labels map[string]string `json:"labels"` TemplatingDriver string `json:"templatingDriver,omitempty"` CreatedAt *int64 `json:"createdAt,omitempty"` UpdatedAt *int64 `json:"updatedAt,omitempty"` } func (c DockerConfigFrontend) NormalizeCollections() DockerConfigFrontend { if c.Labels == nil { c.Labels = map[string]string{} } return c } // DockerSwarmFrontend summarises node-level swarm details. type DockerSwarmFrontend struct { NodeID string `json:"nodeId,omitempty"` NodeRole string `json:"nodeRole,omitempty"` LocalState string `json:"localState,omitempty"` ControlAvailable bool `json:"controlAvailable,omitempty"` ClusterID string `json:"clusterId,omitempty"` ClusterName string `json:"clusterName,omitempty"` Scope string `json:"scope,omitempty"` Error string `json:"error,omitempty"` } // DockerHostCommandFrontend exposes docker host command state to the UI. type DockerHostCommandFrontend struct { ID string `json:"id"` Type string `json:"type"` Status string `json:"status"` Message string `json:"message,omitempty"` CreatedAt int64 `json:"createdAt"` UpdatedAt int64 `json:"updatedAt"` DispatchedAt *int64 `json:"dispatchedAt,omitempty"` AcknowledgedAt *int64 `json:"acknowledgedAt,omitempty"` CompletedAt *int64 `json:"completedAt,omitempty"` FailedAt *int64 `json:"failedAt,omitempty"` FailureReason string `json:"failureReason,omitempty"` ExpiresAt *int64 `json:"expiresAt,omitempty"` } // HostFrontend represents a generic infrastructure host exposed to the UI. type HostFrontend struct { ID string `json:"id"` Hostname string `json:"hostname"` DisplayName string `json:"displayName"` Platform string `json:"platform,omitempty"` OSName string `json:"osName,omitempty"` OSVersion string `json:"osVersion,omitempty"` KernelVersion string `json:"kernelVersion,omitempty"` Architecture string `json:"architecture,omitempty"` CPUCount int `json:"cpuCount,omitempty"` CPUUsage float64 `json:"cpuUsage,omitempty"` LoadAverage []float64 `json:"loadAverage"` Memory *Memory `json:"memory,omitempty"` Disks []Disk `json:"disks"` DiskIO []DiskIO `json:"diskIO"` NetworkInterfaces []HostNetworkInterface `json:"networkInterfaces"` Sensors *HostSensorSummaryFrontend `json:"sensors,omitempty"` Status string `json:"status"` UptimeSeconds int64 `json:"uptimeSeconds,omitempty"` LastSeen int64 `json:"lastSeen"` IntervalSeconds int `json:"intervalSeconds,omitempty"` AgentVersion string `json:"agentVersion,omitempty"` MachineID string `json:"machineId,omitempty"` TokenID string `json:"tokenId,omitempty"` TokenName string `json:"tokenName,omitempty"` TokenHint string `json:"tokenHint,omitempty"` TokenLastUsedAt *int64 `json:"tokenLastUsedAt,omitempty"` Tags []string `json:"tags"` CommandsEnabled bool `json:"commandsEnabled,omitempty"` // Whether AI command execution is enabled IsLegacy bool `json:"isLegacy,omitempty"` // True if using legacy agent protocol LinkedNodeID string `json:"linkedNodeId,omitempty"` // ID of linked PVE node (if running on a node) } func (h HostFrontend) NormalizeCollections() HostFrontend { if h.LoadAverage == nil { h.LoadAverage = []float64{} } if h.Disks == nil { h.Disks = []Disk{} } if h.DiskIO == nil { h.DiskIO = []DiskIO{} } if h.NetworkInterfaces == nil { h.NetworkInterfaces = []HostNetworkInterface{} } if h.Tags == nil { h.Tags = []string{} } if h.Sensors != nil { sensors := h.Sensors.NormalizeCollections() h.Sensors = &sensors } return h } // HostSensorSummaryFrontend mirrors HostSensorSummary with primitives for the frontend. type HostSensorSummaryFrontend struct { TemperatureCelsius map[string]float64 `json:"temperatureCelsius"` FanRPM map[string]float64 `json:"fanRpm"` Additional map[string]float64 `json:"additional"` GPU []HostGPUSensorFrontend `json:"gpu"` ThermalState *HostThermalState `json:"thermalState,omitempty"` SMART []HostDiskSMARTFrontend `json:"smart"` // S.M.A.R.T. disk data } type HostGPUSensorFrontend struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` TemperatureCelsius *float64 `json:"temperatureCelsius,omitempty"` UtilizationPercent *float64 `json:"utilizationPercent,omitempty"` MemoryUsedBytes *int64 `json:"memoryUsedBytes,omitempty"` MemoryTotalBytes *int64 `json:"memoryTotalBytes,omitempty"` } func (s HostSensorSummaryFrontend) NormalizeCollections() HostSensorSummaryFrontend { if s.TemperatureCelsius == nil { s.TemperatureCelsius = map[string]float64{} } if s.FanRPM == nil { s.FanRPM = map[string]float64{} } if s.Additional == nil { s.Additional = map[string]float64{} } if s.GPU == nil { s.GPU = []HostGPUSensorFrontend{} } if s.SMART == nil { s.SMART = []HostDiskSMARTFrontend{} } return s } // HostDiskSMARTFrontend represents S.M.A.R.T. data for a disk from a host agent. type HostDiskSMARTFrontend struct { Device string `json:"device"` // Device name (e.g., sda) Model string `json:"model,omitempty"` // Disk model Serial string `json:"serial,omitempty"` // Serial number WWN string `json:"wwn,omitempty"` // World Wide Name Type string `json:"type,omitempty"` // Transport type: sata, sas, nvme Temperature int `json:"temperature"` // Temperature in Celsius Health string `json:"health,omitempty"` // PASSED, FAILED, UNKNOWN Standby bool `json:"standby,omitempty"` // True if disk was in standby Attributes *SMARTAttributes `json:"attributes,omitempty"` } // StorageFrontend represents Storage with frontend-friendly field names type StorageFrontend struct { ID string `json:"id"` Storage string `json:"storage"` // Maps to Name Name string `json:"name"` Node string `json:"node"` Instance string `json:"instance"` Nodes []string `json:"nodes"` NodeIDs []string `json:"nodeIds"` NodeCount int `json:"nodeCount,omitempty"` Type string `json:"type"` Status string `json:"status"` Pool string `json:"pool,omitempty"` Total int64 `json:"total"` Used int64 `json:"used"` Avail int64 `json:"avail"` // Maps to Free Free int64 `json:"free"` Usage float64 `json:"usage"` Content string `json:"content"` Shared bool `json:"shared"` Enabled bool `json:"enabled"` Active bool `json:"active"` ZFSPool *ZFSPool `json:"zfsPool,omitempty"` } func (s StorageFrontend) NormalizeCollections() StorageFrontend { if s.Nodes == nil { s.Nodes = []string{} } if s.NodeIDs == nil { s.NodeIDs = []string{} } if s.ZFSPool != nil { pool := s.ZFSPool.NormalizeCollections() s.ZFSPool = &pool } return s } // CephClusterFrontend represents a Ceph cluster with frontend-friendly field names type CephClusterFrontend struct { ID string `json:"id"` Instance string `json:"instance"` Name string `json:"name"` FSID string `json:"fsid,omitempty"` Health string `json:"health"` HealthMessage string `json:"healthMessage,omitempty"` TotalBytes int64 `json:"totalBytes"` UsedBytes int64 `json:"usedBytes"` AvailableBytes int64 `json:"availableBytes"` UsagePercent float64 `json:"usagePercent"` NumMons int `json:"numMons"` NumMgrs int `json:"numMgrs"` NumOSDs int `json:"numOsds"` NumOSDsUp int `json:"numOsdsUp"` NumOSDsIn int `json:"numOsdsIn"` NumPGs int `json:"numPGs"` Pools []CephPool `json:"pools"` Services []CephServiceStatus `json:"services"` LastUpdated int64 `json:"lastUpdated"` } func (c CephClusterFrontend) NormalizeCollections() CephClusterFrontend { if c.Pools == nil { c.Pools = []CephPool{} } if c.Services == nil { c.Services = []CephServiceStatus{} } return c } // ReplicationJobFrontend represents a replication job for the frontend. type ReplicationJobFrontend struct { ID string `json:"id"` Instance string `json:"instance"` JobID string `json:"jobId"` JobNumber int `json:"jobNumber,omitempty"` Guest string `json:"guest,omitempty"` GuestID int `json:"guestId,omitempty"` GuestName string `json:"guestName,omitempty"` GuestType string `json:"guestType,omitempty"` GuestNode string `json:"guestNode,omitempty"` SourceNode string `json:"sourceNode,omitempty"` SourceStorage string `json:"sourceStorage,omitempty"` TargetNode string `json:"targetNode,omitempty"` TargetStorage string `json:"targetStorage,omitempty"` Schedule string `json:"schedule,omitempty"` Type string `json:"type,omitempty"` Enabled bool `json:"enabled"` State string `json:"state,omitempty"` Status string `json:"status,omitempty"` LastSyncStatus string `json:"lastSyncStatus,omitempty"` LastSyncTime int64 `json:"lastSyncTime,omitempty"` LastSyncUnix int64 `json:"lastSyncUnix,omitempty"` LastSyncDurationSeconds int `json:"lastSyncDurationSeconds,omitempty"` LastSyncDurationHuman string `json:"lastSyncDurationHuman,omitempty"` NextSyncTime int64 `json:"nextSyncTime,omitempty"` NextSyncUnix int64 `json:"nextSyncUnix,omitempty"` DurationSeconds int `json:"durationSeconds,omitempty"` DurationHuman string `json:"durationHuman,omitempty"` FailCount int `json:"failCount,omitempty"` Error string `json:"error,omitempty"` Comment string `json:"comment,omitempty"` RemoveJob string `json:"removeJob,omitempty"` RateLimitMbps *float64 `json:"rateLimitMbps,omitempty"` PolledAt int64 `json:"polledAt,omitempty"` } // StateFrontend represents the state with frontend-friendly field names type StateFrontend struct { ActiveAlerts []Alert `json:"activeAlerts"` // Active alerts RecentlyResolved []ResolvedAlert `json:"recentlyResolved"` // Recently resolved alerts Metrics []Metric `json:"metrics"` // Time-series metrics Performance Performance `json:"performance"` // Polling/runtime performance ConnectionHealth map[string]bool `json:"connectionHealth"` // Keep as is Stats Stats `json:"stats"` // Runtime statistics LastUpdate int64 `json:"lastUpdate"` // Unix timestamp TemperatureMonitoringEnabled bool `json:"temperatureMonitoringEnabled"` // Global temperature monitoring setting PVETagColors map[string]string `json:"pveTagColors"` // Tag name -> "#rrggbb" from Proxmox datacenter config PVETagStyles map[string]PVETagStyle `json:"pveTagStyles"` // PVE instance -> Proxmox tag style // Unified resources - the new way to access all monitored entities Resources []ResourceFrontend `json:"resources"` ConnectedInfrastructure []ConnectedInfrastructureItemFrontend `json:"connectedInfrastructure"` } // EmptyStateFrontend returns a canonical empty frontend state with stable // collection/object semantics for API and websocket consumers. func EmptyStateFrontend() StateFrontend { return StateFrontend{}.NormalizeCollections() } // NormalizeCollections ensures StateFrontend preserves stable empty collection // semantics instead of leaking nil slices or maps to clients. func (s StateFrontend) NormalizeCollections() StateFrontend { if s.ActiveAlerts == nil { s.ActiveAlerts = []Alert{} } if s.RecentlyResolved == nil { s.RecentlyResolved = []ResolvedAlert{} } if s.Metrics == nil { s.Metrics = []Metric{} } if s.ConnectionHealth == nil { s.ConnectionHealth = map[string]bool{} } if s.PVETagColors == nil { s.PVETagColors = map[string]string{} } if s.PVETagStyles == nil { s.PVETagStyles = map[string]PVETagStyle{} } if s.Performance.APICallDuration == nil { s.Performance.APICallDuration = map[string]float64{} } if s.Resources == nil { s.Resources = []ResourceFrontend{} } if s.ConnectedInfrastructure == nil { s.ConnectedInfrastructure = []ConnectedInfrastructureItemFrontend{} } for i := range s.ConnectedInfrastructure { s.ConnectedInfrastructure[i] = s.ConnectedInfrastructure[i].NormalizeCollections() } for i := range s.Resources { s.Resources[i] = s.Resources[i].NormalizeCollections() } return s } // ResourceFrontend is the frontend representation of a unified Resource. // This mirrors resources.Resource but with time.Time converted to Unix milliseconds. type ResourceFrontend struct { // Identity ID string `json:"id"` Type string `json:"type"` Technology string `json:"technology,omitempty"` Name string `json:"name"` DisplayName string `json:"displayName"` // Platform/Source PlatformID string `json:"platformId"` PlatformType string `json:"platformType"` SourceType string `json:"sourceType"` Sources []string `json:"sources,omitempty"` // Hierarchy ParentID string `json:"parentId,omitempty"` ClusterID string `json:"clusterId,omitempty"` // Universal Metrics Status string `json:"status"` CPU *ResourceMetricFrontend `json:"cpu,omitempty"` Memory *ResourceMetricFrontend `json:"memory,omitempty"` Disk *ResourceMetricFrontend `json:"disk,omitempty"` Network *ResourceNetworkFrontend `json:"network,omitempty"` DiskIO *ResourceDiskIOFrontend `json:"diskIO,omitempty"` Temperature *float64 `json:"temperature,omitempty"` Uptime *int64 `json:"uptime,omitempty"` // Metadata Tags []string `json:"tags"` Labels map[string]string `json:"labels"` CustomURL string `json:"customUrl,omitempty"` LastSeen int64 `json:"lastSeen"` // Unix milliseconds Alerts []ResourceAlertFrontend `json:"alerts"` IncidentCount int `json:"incidentCount,omitempty"` IncidentCode string `json:"incidentCode,omitempty"` IncidentSeverity string `json:"incidentSeverity,omitempty"` IncidentSummary string `json:"incidentSummary,omitempty"` IncidentCategory string `json:"incidentCategory,omitempty"` IncidentLabel string `json:"incidentLabel,omitempty"` IncidentPriority int `json:"incidentPriority,omitempty"` IncidentImpactSummary string `json:"incidentImpactSummary,omitempty"` IncidentUrgency string `json:"incidentUrgency,omitempty"` IncidentAction string `json:"incidentAction,omitempty"` // Identity for deduplication Identity *ResourceIdentityFrontend `json:"identity,omitempty"` DiscoveryTarget json.RawMessage `json:"discoveryTarget,omitempty"` MetricsTarget json.RawMessage `json:"metricsTarget,omitempty"` Canonical json.RawMessage `json:"canonicalIdentity,omitempty"` Policy json.RawMessage `json:"policy,omitempty"` AISafeSummary string `json:"aiSafeSummary,omitempty"` Capabilities json.RawMessage `json:"capabilities,omitempty"` Relationships json.RawMessage `json:"relationships,omitempty"` RecentChanges json.RawMessage `json:"recentChanges,omitempty"` FacetCounts json.RawMessage `json:"facetCounts,omitempty"` Incidents json.RawMessage `json:"incidents,omitempty"` Proxmox json.RawMessage `json:"proxmox,omitempty"` Storage json.RawMessage `json:"storage,omitempty"` Agent json.RawMessage `json:"agent,omitempty"` Docker json.RawMessage `json:"docker,omitempty"` PBS json.RawMessage `json:"pbs,omitempty"` PMG json.RawMessage `json:"pmg,omitempty"` Kubernetes json.RawMessage `json:"kubernetes,omitempty"` PhysicalDisk json.RawMessage `json:"physicalDisk,omitempty"` Ceph json.RawMessage `json:"ceph,omitempty"` TrueNAS json.RawMessage `json:"truenas,omitempty"` VMware json.RawMessage `json:"vmware,omitempty"` Availability json.RawMessage `json:"availability,omitempty"` // Platform-specific data (JSON blob) PlatformData json.RawMessage `json:"platformData,omitempty"` } func (r ResourceFrontend) NormalizeCollections() ResourceFrontend { if r.Tags == nil { r.Tags = []string{} } if r.Labels == nil { r.Labels = map[string]string{} } if r.Alerts == nil { r.Alerts = []ResourceAlertFrontend{} } if r.Identity != nil { identity := r.Identity.NormalizeCollections() r.Identity = &identity } return r } // ResourceMetricFrontend represents a metric value for the frontend. type ResourceMetricFrontend struct { Current float64 `json:"current"` Total *int64 `json:"total,omitempty"` Used *int64 `json:"used,omitempty"` Free *int64 `json:"free,omitempty"` } // ResourceNetworkFrontend represents network metrics for the frontend. type ResourceNetworkFrontend struct { RXBytes int64 `json:"rxBytes"` TXBytes int64 `json:"txBytes"` } // ResourceDiskIOFrontend represents aggregate disk I/O rates for the frontend. type ResourceDiskIOFrontend struct { ReadRate int64 `json:"readRate"` WriteRate int64 `json:"writeRate"` } // ResourceAlertFrontend represents an alert on a resource. type ResourceAlertFrontend struct { ID string `json:"id"` Type string `json:"type"` Level string `json:"level"` Message string `json:"message"` Value float64 `json:"value"` Threshold float64 `json:"threshold"` StartTime int64 `json:"startTime"` // Unix milliseconds } // ResourceIdentityFrontend contains identity info for deduplication. type ResourceIdentityFrontend struct { Hostname string `json:"hostname,omitempty"` MachineID string `json:"machineId,omitempty"` IPs []string `json:"ips"` } func (i ResourceIdentityFrontend) NormalizeCollections() ResourceIdentityFrontend { if i.IPs == nil { i.IPs = []string{} } return i }