diff --git a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js index 099ee534d..86b4b34c0 100644 --- a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js +++ b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js @@ -4,6 +4,8 @@ import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper'; import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models'; import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; +import { getSchemeFromPort } from '@/react/common/network-utils'; + angular.module('portainer.kubernetes').controller('KubernetesApplicationsDatatableController', [ '$scope', '$controller', @@ -105,7 +107,10 @@ angular.module('portainer.kubernetes').controller('KubernetesApplicationsDatatab // Map all load balancer service ports to ip address let loadBalancerURLs = []; if (item.LoadBalancerIPAddress) { - loadBalancerURLs = item.PublishedPorts.map((pp) => `http://${item.LoadBalancerIPAddress}:${pp.Port}`); + loadBalancerURLs = item.PublishedPorts.map((pp) => { + const scheme = getSchemeFromPort(pp.Port); + return `${scheme}://${item.LoadBalancerIPAddress}:${pp.Port}`; + }); } // combine ingress urls diff --git a/app/react/azure/container-instances/ListView/columns/ports.tsx b/app/react/azure/container-instances/ListView/columns/ports.tsx index 24bb8deb7..11d4d8408 100644 --- a/app/react/azure/container-instances/ListView/columns/ports.tsx +++ b/app/react/azure/container-instances/ListView/columns/ports.tsx @@ -3,6 +3,7 @@ import { CellContext } from '@tanstack/react-table'; import { ContainerGroup } from '@/react/azure/types'; import { getPorts } from '@/react/azure/utils'; +import { getSchemeFromPort } from '@/react/common/network-utils'; import { Icon } from '@@/Icon'; @@ -27,10 +28,17 @@ function PortsCell({ return '-'; } - return ports.map((port) => ( - - - {ip}:{port.host} - - )); + return ports.map((port) => { + const scheme = getSchemeFromPort(port.host); + return ( + + + {ip}:{port.host} + + ); + }); } diff --git a/app/react/common/network-utils.ts b/app/react/common/network-utils.ts new file mode 100644 index 000000000..426dc4be7 --- /dev/null +++ b/app/react/common/network-utils.ts @@ -0,0 +1,8 @@ +export function getSchemeFromPort(port?: number): 'http' | 'https' { + if (!port) { + return 'http'; + } + + const hostPort = String(port); + return hostPort.endsWith('443') ? 'https' : 'http'; +} diff --git a/app/react/docker/containers/ListView/ContainersDatatable/columns/ports.tsx b/app/react/docker/containers/ListView/ContainersDatatable/columns/ports.tsx index 0b4f280cc..1c560f873 100644 --- a/app/react/docker/containers/ListView/ContainersDatatable/columns/ports.tsx +++ b/app/react/docker/containers/ListView/ContainersDatatable/columns/ports.tsx @@ -3,6 +3,7 @@ import { ExternalLink } from 'lucide-react'; import { CellContext } from '@tanstack/react-table'; import type { DockerContainer } from '@/react/docker/containers/types'; +import { getSchemeFromPort } from '@/react/common/network-utils'; import { Icon } from '@@/Icon'; @@ -31,18 +32,46 @@ function Cell({ row }: CellContext) { return '-'; } - const { PublicURL: publicUrl } = environment; + const publicURL = getPublicUrl(environment.PublicURL); - return _.uniqBy(ports, 'public').map((port) => ( - - - {port.public}:{port.private} - - )); + return _.uniqBy(ports, 'public').map((port) => { + let url = publicURL || port.host || ''; + if (!url.startsWith('http')) { + const scheme = getSchemeFromPort(port.private); + url = `${scheme}://${url}`; + } + url = `${url}:${port.public}`; + + return ( + + + {port.public}:{port.private} + + ); + }); +} + +function getPublicUrl(url?: string): string { + if (!url) { + return ''; + } + + // Add protocol if missing + const u = + url.startsWith('http://') || url.startsWith('https://') + ? url + : `http://${url}`; + + try { + const parsedUrl = new URL(u); + return `${parsedUrl.protocol}://${parsedUrl.hostname}`; + } catch (error) { + return ''; + } } diff --git a/app/react/docker/services/ListView/ServicesDatatable/columns/ports.tsx b/app/react/docker/services/ListView/ServicesDatatable/columns/ports.tsx index 926d942b3..3f57e3f8f 100644 --- a/app/react/docker/services/ListView/ServicesDatatable/columns/ports.tsx +++ b/app/react/docker/services/ListView/ServicesDatatable/columns/ports.tsx @@ -3,6 +3,7 @@ import { CellContext } from '@tanstack/react-table'; import { ServiceViewModel } from '@/docker/models/service'; import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment'; +import { getSchemeFromPort } from '@/react/common/network-utils'; import { Icon } from '@@/Icon'; @@ -40,16 +41,20 @@ function Cell({ return ports .filter((port) => port.PublishedPort) - .map((port) => ( - - - {port.PublishedPort}:{port.TargetPort} - - )); + .map((port) => { + const scheme = getSchemeFromPort(port.TargetPort); + + return ( + + + {port.PublishedPort}:{port.TargetPort} + + ); + }); }