From b1831d7b3e501e9961129a1bd78390bf7f804fd0 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 5 Nov 2025 19:06:08 +0000 Subject: [PATCH] Add guest URL support for PVE hosts Related to discussion #615 Add optional GuestURL field to PVE instances and cluster endpoints, allowing users to specify a separate guest-accessible URL for web UI navigation that differs from the internal management URL. Backend changes: - Add GuestURL field to PVEInstance and ClusterEndpoint structs - Add GuestURL field to Node model - Update cluster auto-discovery to preserve existing GuestURL values - Update node creation logic to populate GuestURL from config - Update API handlers to accept and persist GuestURL field Frontend changes: - Add GuestURL input field to NodeModal for configuration - Update NodeGroupHeader and NodeSummaryTable to use GuestURL for navigation - Add GuestURL to Node and PVENodeConfig TypeScript interfaces When GuestURL is configured, it will be used for navigation links instead of the Host URL, allowing users to access PVE hosts through a reverse proxy or different domain while maintaining internal API connections. --- .../src/components/Settings/NodeModal.tsx | 25 +++++++++++++++ .../src/components/shared/NodeGroupHeader.tsx | 2 +- .../components/shared/NodeSummaryTable.tsx | 2 +- frontend-modern/src/types/api.ts | 1 + frontend-modern/src/types/nodes.ts | 2 ++ internal/api/config_handlers.go | 32 +++++++++++++++---- internal/config/config.go | 4 ++- internal/models/models.go | 3 +- internal/monitoring/monitor.go | 5 +++ 9 files changed, 66 insertions(+), 10 deletions(-) diff --git a/frontend-modern/src/components/Settings/NodeModal.tsx b/frontend-modern/src/components/Settings/NodeModal.tsx index 725ea39..29a0923 100644 --- a/frontend-modern/src/components/Settings/NodeModal.tsx +++ b/frontend-modern/src/components/Settings/NodeModal.tsx @@ -65,6 +65,7 @@ export const NodeModal: Component = (props) => { const getCleanFormData = (nodeType: 'pve' | 'pbs' | 'pmg' = props.nodeType) => ({ name: '', host: '', + guestURL: '', authType: nodeType === 'pmg' ? 'password' : ('token' as 'password' | 'token'), setupMode: 'auto' as 'auto' | 'manual', user: '', @@ -172,6 +173,7 @@ export const NodeModal: Component = (props) => { setFormData({ name: node.name || '', host: node.host || '', + guestURL: ('guestURL' in node ? node.guestURL : '') || '', authType: node.hasPassword ? 'password' : 'token', setupMode: 'auto', user: username, @@ -211,6 +213,7 @@ export const NodeModal: Component = (props) => { type: props.nodeType, name: normalizedName, host: data.host, + guestURL: data.guestURL, fingerprint: data.fingerprint, verifySSL: data.verifySSL, }; @@ -489,6 +492,28 @@ export const NodeModal: Component = (props) => {

+ +
+ + updateField('guestURL', e.currentTarget.value)} + placeholder={ + props.nodeType === 'pve' + ? 'https://pve.yourdomain.com' + : props.nodeType === 'pbs' + ? 'https://pbs.yourdomain.com' + : 'https://pmg.yourdomain.com' + } + class={controlClass()} + /> +

+ Optional guest-accessible URL for navigation. If specified, this URL will be used when opening the web UI instead of the Host URL. +

+
diff --git a/frontend-modern/src/components/shared/NodeGroupHeader.tsx b/frontend-modern/src/components/shared/NodeGroupHeader.tsx index 5777f0f..ae1d6f9 100644 --- a/frontend-modern/src/components/shared/NodeGroupHeader.tsx +++ b/frontend-modern/src/components/shared/NodeGroupHeader.tsx @@ -9,7 +9,7 @@ interface NodeGroupHeaderProps { export const NodeGroupHeader: Component = (props) => { const isOnline = () => props.node.status === 'online' && (props.node.uptime || 0) > 0; - const nodeUrl = () => props.node.host || `https://${props.node.name}:8006`; + const nodeUrl = () => props.node.guestURL || props.node.host || `https://${props.node.name}:8006`; const displayName = () => getNodeDisplayName(props.node); const showActualName = () => hasAlternateDisplayName(props.node); diff --git a/frontend-modern/src/components/shared/NodeSummaryTable.tsx b/frontend-modern/src/components/shared/NodeSummaryTable.tsx index c9bfc96..dd9534f 100644 --- a/frontend-modern/src/components/shared/NodeSummaryTable.tsx +++ b/frontend-modern/src/components/shared/NodeSummaryTable.tsx @@ -509,7 +509,7 @@ export const NodeSummaryTable: Component = (props) => { 0 { for _, ep := range instanceCfg.ClusterEndpoints { if strings.EqualFold(ep.NodeName, node.Node) { if effective := clusterEndpointEffectiveURL(ep); effective != "" { connectionHost = effective } + if ep.GuestURL != "" { + guestURL = ep.GuestURL + } break } } @@ -4939,6 +4943,7 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie DisplayName: displayName, Instance: instanceName, Host: connectionHost, + GuestURL: guestURL, Status: effectiveStatus, Type: "node", CPU: safeFloat(node.CPU), // Already in percentage