mirror of
https://github.com/fosrl/pangolin.git
synced 2026-10-22 20:49:11 +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 SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
import { internal } from "@app/lib/api";
|
import { internal } from "@app/lib/api";
|
||||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
|
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||||
import { GetDomainResponse } from "@server/routers/domain/getDomain";
|
import { GetDomainResponse } from "@server/routers/domain/getDomain";
|
||||||
import type { ListRedirectsResponse } from "@server/routers/redirect";
|
import type { ListRedirectsResponse } from "@server/routers/redirect";
|
||||||
import { GetResourceResponse } from "@server/routers/resource/getResource";
|
import { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||||
@@ -127,6 +128,50 @@ export default async function RedirectIndexPage(props: RedirectIndexPageProps) {
|
|||||||
baseDomain: redirect.baseDomain
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsSectionTitle
|
<SettingsSectionTitle
|
||||||
@@ -144,6 +189,7 @@ export default async function RedirectIndexPage(props: RedirectIndexPageProps) {
|
|||||||
}}
|
}}
|
||||||
initialFilterResource={initialFilterResource}
|
initialFilterResource={initialFilterResource}
|
||||||
initialFilterDomain={initialFilterDomain}
|
initialFilterDomain={initialFilterDomain}
|
||||||
|
initialCertificates={initialCertificates}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { internal } from "@app/lib/api";
|
|||||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
||||||
import OrgProvider from "@app/providers/OrgProvider";
|
import OrgProvider from "@app/providers/OrgProvider";
|
||||||
import { build } from "@server/build";
|
|
||||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||||
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
|
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
|
||||||
import type { AxiosResponse } from "axios";
|
import type { AxiosResponse } from "axios";
|
||||||
@@ -120,7 +119,7 @@ export default async function ClientResourcesPage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
||||||
if (build !== "oss" && certDomains.length > 0) {
|
if (certDomains.length > 0) {
|
||||||
try {
|
try {
|
||||||
const certSearchParams = new URLSearchParams(
|
const certSearchParams = new URLSearchParams(
|
||||||
certDomains.map((domain) => ["domains", domain])
|
certDomains.map((domain) => ["domains", domain])
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import PublicResourcesBanner from "@app/components/PublicResourcesBanner";
|
|||||||
import { internal } from "@app/lib/api";
|
import { internal } from "@app/lib/api";
|
||||||
import { authCookieHeader } from "@app/lib/api/cookies";
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
import OrgProvider from "@app/providers/OrgProvider";
|
import OrgProvider from "@app/providers/OrgProvider";
|
||||||
import { build } from "@server/build";
|
|
||||||
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||||
import type { GetOrgResponse } from "@server/routers/org";
|
import type { GetOrgResponse } from "@server/routers/org";
|
||||||
import type { ListResourcesResponse } from "@server/routers/resource";
|
import type { ListResourcesResponse } from "@server/routers/resource";
|
||||||
@@ -132,7 +131,7 @@ export default async function ProxyResourcesPage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
let initialCertificates: GetBatchedCertificateResponse | undefined;
|
||||||
if (build !== "oss" && certDomains.length > 0) {
|
if (certDomains.length > 0) {
|
||||||
try {
|
try {
|
||||||
const certSearchParams = new URLSearchParams(
|
const certSearchParams = new URLSearchParams(
|
||||||
certDomains.map((domain) => ["domains", domain])
|
certDomains.map((domain) => ["domains", domain])
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||||
|
import { ResourceAccessCertIndicator } from "@app/components/ResourceAccessCertIndicator";
|
||||||
import {
|
import {
|
||||||
ResourceSelector,
|
ResourceSelector,
|
||||||
type SelectedResource
|
type SelectedResource
|
||||||
@@ -40,6 +41,7 @@ import { cn } from "@app/lib/cn";
|
|||||||
import { dataTableFilterPopoverContentClassName } from "@app/lib/dataTableFilterPopover";
|
import { dataTableFilterPopoverContentClassName } from "@app/lib/dataTableFilterPopover";
|
||||||
import { orgQueries } from "@app/lib/queries";
|
import { orgQueries } from "@app/lib/queries";
|
||||||
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
||||||
|
import type { GetBatchedCertificateResponse } from "@server/routers/certificates/types";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type { PaginationState } from "@tanstack/react-table";
|
import type { PaginationState } from "@tanstack/react-table";
|
||||||
import {
|
import {
|
||||||
@@ -113,6 +115,8 @@ type RedirectsTableProps = {
|
|||||||
rowCount: number;
|
rowCount: number;
|
||||||
initialFilterResource?: SelectedResource | null;
|
initialFilterResource?: SelectedResource | null;
|
||||||
initialFilterDomain?: RedirectFilterDomain | null;
|
initialFilterDomain?: RedirectFilterDomain | null;
|
||||||
|
/** Certificates prefetched on the server, keyed by full domain. */
|
||||||
|
initialCertificates?: GetBatchedCertificateResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RedirectsTable({
|
export default function RedirectsTable({
|
||||||
@@ -121,7 +125,8 @@ export default function RedirectsTable({
|
|||||||
pagination,
|
pagination,
|
||||||
rowCount,
|
rowCount,
|
||||||
initialFilterResource = null,
|
initialFilterResource = null,
|
||||||
initialFilterDomain = null
|
initialFilterDomain = null,
|
||||||
|
initialCertificates
|
||||||
}: RedirectsTableProps) {
|
}: RedirectsTableProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -460,7 +465,36 @@ export default function RedirectsTable({
|
|||||||
return <span>-</span>;
|
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,
|
domainFilterOpen,
|
||||||
resourceFilterOpen,
|
resourceFilterOpen,
|
||||||
selectedDomain,
|
selectedDomain,
|
||||||
selectedResource
|
selectedResource,
|
||||||
|
initialCertificates
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user