From 435ddeee340ac266d0fd6e405ac7c0ea15edbb5c Mon Sep 17 00:00:00 2001 From: "zarges.s" Date: Mon, 2 Nov 2026 13:16:22 +0100 Subject: [PATCH 1/2] feat: show certificate details in host tables The SSL column of the proxy, redirection, 404 and stream host tables only showed the certificate type, so it was not possible to tell which certificate a host uses. - Popover on the SSL column with name, ID, provider (incl. DNS provider), domain names and expiry date - SSL label turns red when the certificate is expired and yellow when it expires within 30 days - Custom certificates show their name below the label Refs #5705 Co-Authored-By: Claude Opus 5.5 --- .../Table/Formatter/CertificateFormatter.tsx | 99 +++++++++++++++++-- 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Table/Formatter/CertificateFormatter.tsx b/frontend/src/components/Table/Formatter/CertificateFormatter.tsx index 6b791bfbf..a29bdc082 100644 --- a/frontend/src/components/Table/Formatter/CertificateFormatter.tsx +++ b/frontend/src/components/Table/Formatter/CertificateFormatter.tsx @@ -1,18 +1,97 @@ +import cn from "classnames"; +import { differenceInDays, isPast } from "date-fns"; +import OverlayTrigger from "react-bootstrap/OverlayTrigger"; +import Popover from "react-bootstrap/Popover"; import type { Certificate } from "src/api/backend"; -import { T } from "src/locale"; +import { DateFormatter } from "src/components"; +import { parseDate, T } from "src/locale"; + +const getProviderLabel = (provider: string) => { + if (provider === "letsencrypt") { + return "lets-encrypt"; + } + if (provider === "other") { + return "certificates.custom"; + } + return provider; +}; interface Props { certificate?: Certificate; } export function CertificateFormatter({ certificate }: Props) { - let translation = "http-only"; - if (certificate) { - translation = certificate.provider; - if (translation === "letsencrypt") { - translation = "lets-encrypt"; - } else if (translation === "other") { - translation = "certificates.custom"; - } + if (!certificate) { + return ; } - return ; + + const translation = getProviderLabel(certificate.provider); + const expires = certificate.expiresOn ? parseDate(certificate.expiresOn) : null; + const expired = expires ? isPast(expires) : false; + const daysLeft = expires ? differenceInDays(expires, new Date()) : null; + const labelClass = cn({ + "text-danger": expired, + "text-warning": !expired && daysLeft !== null && daysLeft <= 30, + }); + + const dnsProvider = certificate.meta?.dnsChallenge ? certificate.meta?.dnsProvider : null; + const name = certificate.niceName || certificate.domainNames?.[0] || `#${certificate.id}`; + + const popover = ( + + +
+ {name} + #{certificate.id} +
+
+ + + +
+
+ + {dnsProvider ? ` (DNS: ${dnsProvider})` : null} +
+ {certificate.domainNames?.length ? ( + <> +
+ + + +
+ {certificate.domainNames.map((domain) => ( +
+ {domain} +
+ ))} + + ) : null} + {certificate.expiresOn ? ( + <> +
+ + + +
+
+ +
+ + ) : null} +
+
+ ); + + return ( + +
+ + + + {certificate.provider === "other" && certificate.niceName ? ( +
{certificate.niceName}
+ ) : null} +
+
+ ); } From d0739b7210708589f7ba9718ad8e36bb3d39af4d Mon Sep 17 00:00:00 2001 From: "zarges.s" Date: Mon, 2 Nov 2026 13:36:15 +0100 Subject: [PATCH 2/2] fix: drop DNS provider from certificate popover Host endpoints clear the certificate meta, so the DNS provider is never available in the host tables. Co-Authored-By: Claude Opus 5.5 --- .../src/components/Table/Formatter/CertificateFormatter.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/Table/Formatter/CertificateFormatter.tsx b/frontend/src/components/Table/Formatter/CertificateFormatter.tsx index a29bdc082..9a2d50849 100644 --- a/frontend/src/components/Table/Formatter/CertificateFormatter.tsx +++ b/frontend/src/components/Table/Formatter/CertificateFormatter.tsx @@ -33,7 +33,6 @@ export function CertificateFormatter({ certificate }: Props) { "text-warning": !expired && daysLeft !== null && daysLeft <= 30, }); - const dnsProvider = certificate.meta?.dnsChallenge ? certificate.meta?.dnsProvider : null; const name = certificate.niceName || certificate.domainNames?.[0] || `#${certificate.id}`; const popover = ( @@ -50,7 +49,6 @@ export function CertificateFormatter({ certificate }: Props) {
- {dnsProvider ? ` (DNS: ${dnsProvider})` : null}
{certificate.domainNames?.length ? ( <>