diff --git a/src/app/[orgId]/settings/resources/private/page.tsx b/src/app/[orgId]/settings/resources/private/page.tsx index 348340aa9..cbd9ec7ef 100644 --- a/src/app/[orgId]/settings/resources/private/page.tsx +++ b/src/app/[orgId]/settings/resources/private/page.tsx @@ -6,6 +6,8 @@ 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"; import type { Metadata } from "next"; @@ -99,6 +101,42 @@ export default async function ClientResourcesPage( }; } ); + + // 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( + internalResourceRows + .filter( + (r) => + r.mode === "http" && + !r.alias && + r.ssl && + r.domainId && + r.fullDomain + ) + .map((r) => r.fullDomain as string) + ) + ); + + let initialCertificates: GetBatchedCertificateResponse | undefined; + if (build !== "oss" && certDomains.length > 0) { + try { + const certSearchParams = new URLSearchParams( + certDomains.map((domain) => ["domains", domain]) + ); + const certRes = await internal.get< + AxiosResponse + >( + `/org/${params.orgId}/batched-certificates?${certSearchParams.toString()}`, + await authCookieHeader() + ); + initialCertificates = certRes.data.data; + } catch { + // leave undefined so each row falls back to fetching its own + } + } + return ( <> diff --git a/src/components/PrivateResourcesTable.tsx b/src/components/PrivateResourcesTable.tsx index 6c2999624..5ee23f424 100644 --- a/src/components/PrivateResourcesTable.tsx +++ b/src/components/PrivateResourcesTable.tsx @@ -37,6 +37,7 @@ import { getPrivateResourceSettingsHref } from "@app/lib/launcherResourceAdminHr import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn"; import { build } from "@server/build"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; +import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types"; import { UpdateSiteResourceResponse } from "@server/routers/siteResource"; import type { PaginationState } from "@tanstack/react-table"; import { AxiosResponse } from "axios"; @@ -136,13 +137,16 @@ type ClientResourcesTableProps = { orgId: string; pagination: PaginationState; rowCount: number; + /** Certificates prefetched on the server, keyed by full domain. */ + initialCertificates?: GetBatchedCertificateResponse; }; export default function PrivateResourcesTable({ internalResources, orgId, pagination, - rowCount + rowCount, + initialCertificates }: ClientResourcesTableProps) { const router = useRouter(); const { @@ -432,6 +436,9 @@ export default function PrivateResourcesTable({ orgId={resourceRow.orgId} domainId={domainId} fullDomain={fullDomain} + initialCertValue={ + initialCertificates?.[fullDomain] + } /> ) : null}
@@ -585,7 +592,7 @@ export default function PrivateResourcesTable({ ]; return cols; - }, [orgId, t, searchParams]); + }, [orgId, t, searchParams, initialCertificates]); function handleFilterChange( column: string,