mirror of
https://github.com/fosrl/pangolin.git
synced 2026-10-21 20:19:07 +02:00
add cert status to redirects
This commit is contained in:
@@ -5,6 +5,7 @@ import type { SelectedResource } from "@app/components/resource-selector";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||
import { GetDomainResponse } from "@server/routers/domain/getDomain";
|
||||
import type { ListRedirectsResponse } from "@server/routers/redirect";
|
||||
import { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||
@@ -127,6 +128,50 @@ export default async function RedirectIndexPage(props: RedirectIndexPageProps) {
|
||||
baseDomain: redirect.baseDomain
|
||||
}));
|
||||
|
||||
// Prefetched in one batched call so the table doesn't fire a separate
|
||||
// certificate request per visible row once it mounts on the client.
|
||||
const certDomains = Array.from(
|
||||
new Set(
|
||||
redirectRows
|
||||
.map((redirect) => {
|
||||
if (redirect.resourceId) {
|
||||
if (
|
||||
redirect.resourceSsl &&
|
||||
redirect.resourceFullDomain
|
||||
) {
|
||||
return redirect.resourceFullDomain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!redirect.ssl || !redirect.baseDomain) {
|
||||
return null;
|
||||
}
|
||||
return [redirect.subdomain, redirect.baseDomain]
|
||||
.filter(Boolean)
|
||||
.join(".");
|
||||
})
|
||||
.filter((domain): domain is string => Boolean(domain))
|
||||
)
|
||||
);
|
||||
|
||||
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
||||
if (certDomains.length > 0) {
|
||||
try {
|
||||
const certSearchParams = new URLSearchParams(
|
||||
certDomains.map((domain) => ["domains", domain])
|
||||
);
|
||||
const certRes = await internal.get<
|
||||
AxiosResponse<GetBatchedCertificateResponse>
|
||||
>(
|
||||
`/org/${orgId}/batched-certificates?${certSearchParams.toString()}`,
|
||||
header
|
||||
);
|
||||
initialCertificates = certRes.data.data;
|
||||
} catch {
|
||||
// leave undefined so each row falls back to fetching its own
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSectionTitle
|
||||
@@ -144,6 +189,7 @@ export default async function RedirectIndexPage(props: RedirectIndexPageProps) {
|
||||
}}
|
||||
initialFilterResource={initialFilterResource}
|
||||
initialFilterDomain={initialFilterDomain}
|
||||
initialCertificates={initialCertificates}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
||||
import OrgProvider from "@app/providers/OrgProvider";
|
||||
import { build } from "@server/build";
|
||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
|
||||
import type { AxiosResponse } from "axios";
|
||||
@@ -120,7 +119,7 @@ export default async function ClientResourcesPage(
|
||||
);
|
||||
|
||||
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
||||
if (build !== "oss" && certDomains.length > 0) {
|
||||
if (certDomains.length > 0) {
|
||||
try {
|
||||
const certSearchParams = new URLSearchParams(
|
||||
certDomains.map((domain) => ["domains", domain])
|
||||
|
||||
@@ -5,7 +5,6 @@ import PublicResourcesBanner from "@app/components/PublicResourcesBanner";
|
||||
import { internal } from "@app/lib/api";
|
||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import OrgProvider from "@app/providers/OrgProvider";
|
||||
import { build } from "@server/build";
|
||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||
import type { GetOrgResponse } from "@server/routers/org";
|
||||
import type { ListResourcesResponse } from "@server/routers/resource";
|
||||
@@ -132,7 +131,7 @@ export default async function ProxyResourcesPage(
|
||||
);
|
||||
|
||||
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
||||
if (build !== "oss" && certDomains.length > 0) {
|
||||
if (certDomains.length > 0) {
|
||||
try {
|
||||
const certSearchParams = new URLSearchParams(
|
||||
certDomains.map((domain) => ["domains", domain])
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import { ResourceAccessCertIndicator } from "@app/components/ResourceAccessCertIndicator";
|
||||
import {
|
||||
ResourceSelector,
|
||||
type SelectedResource
|
||||
@@ -40,6 +41,7 @@ import { cn } from "@app/lib/cn";
|
||||
import { dataTableFilterPopoverContentClassName } from "@app/lib/dataTableFilterPopover";
|
||||
import { orgQueries } from "@app/lib/queries";
|
||||
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
@@ -113,6 +115,8 @@ type RedirectsTableProps = {
|
||||
rowCount: number;
|
||||
initialFilterResource?: SelectedResource | null;
|
||||
initialFilterDomain?: RedirectFilterDomain | null;
|
||||
/** Certificates prefetched on the server, keyed by full domain. */
|
||||
initialCertificates?: GetBatchedCertificateResponse;
|
||||
};
|
||||
|
||||
export default function RedirectsTable({
|
||||
@@ -121,7 +125,8 @@ export default function RedirectsTable({
|
||||
pagination,
|
||||
rowCount,
|
||||
initialFilterResource = null,
|
||||
initialFilterDomain = null
|
||||
initialFilterDomain = null,
|
||||
initialCertificates
|
||||
}: RedirectsTableProps) {
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
@@ -460,7 +465,36 @@ export default function RedirectsTable({
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
return <CopyToClipboard text={source} isLink={false} />;
|
||||
const certDomainId = redirect.resourceId
|
||||
? redirect.resourceDomainId
|
||||
: redirect.domainId;
|
||||
const certHostname = redirect.resourceId
|
||||
? redirect.resourceFullDomain
|
||||
: domainHost;
|
||||
const showHttpsCertIndicator =
|
||||
sourceSsl &&
|
||||
certHostname != null &&
|
||||
certHostname !== "" &&
|
||||
certDomainId != null &&
|
||||
certDomainId !== "";
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{showHttpsCertIndicator ? (
|
||||
<ResourceAccessCertIndicator
|
||||
orgId={orgId}
|
||||
domainId={certDomainId}
|
||||
fullDomain={certHostname}
|
||||
initialCertValue={
|
||||
initialCertificates?.[certHostname]
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<div className="">
|
||||
<CopyToClipboard text={source} isLink={false} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -647,7 +681,8 @@ export default function RedirectsTable({
|
||||
domainFilterOpen,
|
||||
resourceFilterOpen,
|
||||
selectedDomain,
|
||||
selectedResource
|
||||
selectedResource,
|
||||
initialCertificates
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user