mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2026-11-15 10:58:22 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
1aca775cb9
commit
435ddeee34
1 file changed
+89
-10
@@ -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 <T id="http-only" />;
|
||||
}
|
||||
return <T id={translation} />;
|
||||
|
||||
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 = (
|
||||
<Popover id={`popover-certificate-${certificate.id}`}>
|
||||
<Popover.Body>
|
||||
<div className="mb-2">
|
||||
<strong>{name}</strong>
|
||||
<span className="text-secondary ms-1">#{certificate.id}</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>
|
||||
<T id="column.provider" />
|
||||
</strong>
|
||||
</div>
|
||||
<div className="ms-1 mb-2">
|
||||
<T id={translation} />
|
||||
{dnsProvider ? ` (DNS: ${dnsProvider})` : null}
|
||||
</div>
|
||||
{certificate.domainNames?.length ? (
|
||||
<>
|
||||
<div>
|
||||
<strong>
|
||||
<T id="domain-names" />
|
||||
</strong>
|
||||
</div>
|
||||
{certificate.domainNames.map((domain) => (
|
||||
<div key={domain} className="ms-1">
|
||||
{domain}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
{certificate.expiresOn ? (
|
||||
<>
|
||||
<div className="mt-2">
|
||||
<strong>
|
||||
<T id="column.expires" />
|
||||
</strong>
|
||||
</div>
|
||||
<div className="ms-1">
|
||||
<DateFormatter value={certificate.expiresOn} highlightPast highlistNearlyExpired />
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
return (
|
||||
<OverlayTrigger trigger={["hover", "click", "focus"]} placement="bottom" overlay={popover}>
|
||||
<div style={{ cursor: "help" }}>
|
||||
<span className={labelClass}>
|
||||
<T id={translation} />
|
||||
</span>
|
||||
{certificate.provider === "other" && certificate.niceName ? (
|
||||
<div className="text-secondary small">{certificate.niceName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user