From e4732af0f55360ed3e53902608e34238d89afd43 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 22 Dec 2025 22:05:25 +0000 Subject: [PATCH] fix: use configured Guest URLs for PVE/PBS/PMG navigation (#870) - Fix PVE nodes: buildNodeUrl in ProxmoxNodesSection.tsx now prioritizes guestURL over host (was ignoring guestURL entirely) - Add PBS support: GuestURL field added to PBSInstance config, model, and API handlers - Add PMG support: GuestURL field added to PMGInstance config, model, and API handlers - Update NodeSummaryTable to use guestURL for PBS nodes - Frontend types updated for PBS/PMG guestURL support The Guest URL setting in node configuration now works correctly across all node types. When set, it takes priority over the Host URL when clicking on node names to navigate to the Proxmox/PBS/PMG web UI. Closes #870 --- .../Thresholds/sections/ProxmoxNodesSection.tsx | 11 +++++++++++ .../src/components/shared/NodeSummaryTable.tsx | 2 +- frontend-modern/src/types/api.ts | 2 ++ frontend-modern/src/types/nodes.ts | 2 ++ internal/api/config_handlers.go | 10 ++++++++++ internal/config/config.go | 2 ++ internal/models/models.go | 2 ++ internal/monitoring/monitor.go | 2 ++ 8 files changed, 32 insertions(+), 1 deletion(-) diff --git a/frontend-modern/src/components/Alerts/Thresholds/sections/ProxmoxNodesSection.tsx b/frontend-modern/src/components/Alerts/Thresholds/sections/ProxmoxNodesSection.tsx index d278b622c..bafc28dd0 100644 --- a/frontend-modern/src/components/Alerts/Thresholds/sections/ProxmoxNodesSection.tsx +++ b/frontend-modern/src/components/Alerts/Thresholds/sections/ProxmoxNodesSection.tsx @@ -119,12 +119,23 @@ const nodeToResource = ( * Build management URL for a node */ const buildNodeUrl = (node: Node): string | undefined => { + // Prioritize guestURL if available + const guestUrlValue = node.guestURL?.trim(); + if (guestUrlValue) { + return guestUrlValue.startsWith('http') + ? guestUrlValue + : `https://${guestUrlValue}`; + } + + // Fallback to host const hostValue = node.host?.trim(); if (hostValue) { return hostValue.startsWith('http') ? hostValue : `https://${hostValue.includes(':') ? hostValue : `${hostValue}:8006`}`; } + + // Final fallback to node name if (node.name) { return `https://${node.name.includes(':') ? node.name : `${node.name}:8006`}`; } diff --git a/frontend-modern/src/components/shared/NodeSummaryTable.tsx b/frontend-modern/src/components/shared/NodeSummaryTable.tsx index 4206bafd0..b290d525e 100644 --- a/frontend-modern/src/components/shared/NodeSummaryTable.tsx +++ b/frontend-modern/src/components/shared/NodeSummaryTable.tsx @@ -499,7 +499,7 @@ export const NodeSummaryTable: Component = (props) => { href={ isPVEItem ? node!.guestURL || node!.host || `https://${node!.name}:8006` - : pbs!.host || `https://${pbs!.name}:8007` + : pbs!.guestURL || pbs!.host || `https://${pbs!.name}:8007` } target="_blank" onClick={(e) => e.stopPropagation()} diff --git a/frontend-modern/src/types/api.ts b/frontend-modern/src/types/api.ts index e081b8831..ef4209a9a 100644 --- a/frontend-modern/src/types/api.ts +++ b/frontend-modern/src/types/api.ts @@ -635,6 +635,7 @@ export interface PBSInstance { id: string; name: string; host: string; + guestURL?: string; // Optional guest-accessible URL (for navigation) status: string; version: string; cpu: number; @@ -656,6 +657,7 @@ export interface PMGInstance { id: string; name: string; host: string; + guestURL?: string; // Optional guest-accessible URL (for navigation) status: string; version: string; nodes?: PMGNodeStatus[]; diff --git a/frontend-modern/src/types/nodes.ts b/frontend-modern/src/types/nodes.ts index cf53e3896..8fcca64dd 100644 --- a/frontend-modern/src/types/nodes.ts +++ b/frontend-modern/src/types/nodes.ts @@ -50,6 +50,7 @@ export interface PBSNodeConfig { id: string; name: string; host: string; + guestURL?: string; user: string; hasPassword?: boolean; hasToken?: boolean; @@ -70,6 +71,7 @@ export interface PMGNodeConfig { id: string; name: string; host: string; + guestURL?: string; user: string; hasPassword?: boolean; hasToken?: boolean; diff --git a/internal/api/config_handlers.go b/internal/api/config_handlers.go index 0150c45f5..b15b81522 100644 --- a/internal/api/config_handlers.go +++ b/internal/api/config_handlers.go @@ -847,6 +847,7 @@ func (h *ConfigHandlers) GetAllNodesForAPI() []NodeResponse { Type: "pbs", Name: pbs.Name, Host: pbs.Host, + GuestURL: pbs.GuestURL, User: pbs.User, HasPassword: pbs.Password != "", TokenName: pbs.TokenName, @@ -878,6 +879,7 @@ func (h *ConfigHandlers) GetAllNodesForAPI() []NodeResponse { Type: "pmg", Name: pmgInst.Name, Host: pmgInst.Host, + GuestURL: pmgInst.GuestURL, User: pmgInst.User, HasPassword: pmgInst.Password != "", TokenName: pmgInst.TokenName, @@ -1486,6 +1488,7 @@ func (h *ConfigHandlers) HandleAddNode(w http.ResponseWriter, r *http.Request) { pbs := config.PBSInstance{ Name: req.Name, Host: host, + GuestURL: req.GuestURL, User: pbsUser, Password: pbsPassword, TokenName: pbsTokenName, @@ -1556,6 +1559,7 @@ func (h *ConfigHandlers) HandleAddNode(w http.ResponseWriter, r *http.Request) { pmgInstance := config.PMGInstance{ Name: req.Name, Host: host, + GuestURL: req.GuestURL, User: pmgUser, Password: pmgPassword, TokenName: pmgTokenName, @@ -2030,6 +2034,9 @@ func (h *ConfigHandlers) HandleUpdateNode(w http.ResponseWriter, r *http.Request } pbs.Host = host + // Update GuestURL if provided + pbs.GuestURL = req.GuestURL + // Handle authentication updates - only switch auth method if explicitly provided if req.TokenName != "" && req.TokenValue != "" { // Switching to token authentication @@ -2110,6 +2117,9 @@ func (h *ConfigHandlers) HandleUpdateNode(w http.ResponseWriter, r *http.Request pmgInst.Host = host } + // Update GuestURL if provided + pmgInst.GuestURL = req.GuestURL + // Handle authentication updates - only switch auth method if explicitly provided if req.TokenName != "" && req.TokenValue != "" { // Switching to token authentication diff --git a/internal/config/config.go b/internal/config/config.go index 24dd1c594..a54da55f5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -462,6 +462,7 @@ type ClusterEndpoint struct { type PBSInstance struct { Name string Host string + GuestURL string // Optional guest-accessible URL (for navigation) User string Password string TokenName string @@ -485,6 +486,7 @@ type PBSInstance struct { type PMGInstance struct { Name string Host string + GuestURL string // Optional guest-accessible URL (for navigation) User string Password string TokenName string diff --git a/internal/models/models.go b/internal/models/models.go index 0a0eeca1b..c076d6140 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -760,6 +760,7 @@ type PBSInstance struct { ID string `json:"id"` Name string `json:"name"` Host string `json:"host"` + GuestURL string `json:"guestURL,omitempty"` // Optional guest-accessible URL (for navigation) Status string `json:"status"` Version string `json:"version"` CPU float64 `json:"cpu"` // CPU usage percentage @@ -873,6 +874,7 @@ type PMGInstance struct { ID string `json:"id"` Name string `json:"name"` Host string `json:"host"` + GuestURL string `json:"guestURL,omitempty"` // Optional guest-accessible URL (for navigation) Status string `json:"status"` Version string `json:"version"` Nodes []PMGNodeStatus `json:"nodes,omitempty"` diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index d538e811d..edbff1288 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -7006,6 +7006,7 @@ func (m *Monitor) pollPBSInstance(ctx context.Context, instanceName string, clie ID: "pbs-" + instanceName, Name: instanceName, Host: instanceCfg.Host, + GuestURL: instanceCfg.GuestURL, Status: "offline", Version: "unknown", ConnectionHealth: "unhealthy", @@ -7336,6 +7337,7 @@ func (m *Monitor) pollPMGInstance(ctx context.Context, instanceName string, clie ID: "pmg-" + instanceName, Name: instanceName, Host: instanceCfg.Host, + GuestURL: instanceCfg.GuestURL, Status: "offline", ConnectionHealth: "unhealthy", LastSeen: now,