From 835a30cffe95ff29b9b41bf27b19bc9b74017fb7 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 4 Aug 2026 17:44:34 -0400 Subject: [PATCH] Show the cert status of the namespace domains properly --- .../certificates/getBatchedCertificates.ts | 32 ++++++++++++++++--- src/hooks/useCertificate.ts | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/server/private/routers/certificates/getBatchedCertificates.ts b/server/private/routers/certificates/getBatchedCertificates.ts index 1c390b7dd..2ab5fd288 100644 --- a/server/private/routers/certificates/getBatchedCertificates.ts +++ b/server/private/routers/certificates/getBatchedCertificates.ts @@ -10,12 +10,12 @@ * * This file is not licensed under the AGPLv3. */ -import { certificates, db, domains, orgDomains } from "@server/db"; +import { certificates, db, domainNamespaces, domains, orgDomains } from "@server/db"; import response from "@server/lib/response"; import logger from "@server/logger"; import { type GetBatchedCertificateResponse } from "@server/routers/certificates/types"; import HttpCode from "@server/types/HttpCode"; -import { and, eq, inArray, or } from "drizzle-orm"; +import { and, eq, inArray, isNotNull, or } from "drizzle-orm"; import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; @@ -63,14 +63,28 @@ async function query(orgId: string, domainList: string[]) { }) .from(certificates) .innerJoin(domains, eq(certificates.domainId, domains.domainId)) - .innerJoin( + .leftJoin( orgDomains, and( eq(domains.domainId, orgDomains.domainId), eq(orgDomains.orgId, orgId) ) ) - .where(and(inArray(certificates.domain, domainList))); + .leftJoin( + domainNamespaces, + eq(domains.domainId, domainNamespaces.domainId) + ) + .where( + and( + inArray(certificates.domain, domainList), + // Namespace domains are shared across all orgs, so they skip + // the org-ownership check (mirrors verifyCertificateAccess). + or( + isNotNull(orgDomains.orgId), + isNotNull(domainNamespaces.domainNamespaceId) + ) + ) + ); // All non resolved domain certificates might be `ns` or `wildcard`, // which means exact domain certificates do not exist @@ -110,19 +124,27 @@ async function query(orgId: string, domainList: string[]) { }) .from(certificates) .innerJoin(domains, eq(certificates.domainId, domains.domainId)) - .innerJoin( + .leftJoin( orgDomains, and( eq(domains.domainId, orgDomains.domainId), eq(orgDomains.orgId, orgId) ) ) + .leftJoin( + domainNamespaces, + eq(domains.domainId, domainNamespaces.domainId) + ) .where( and( eq(certificates.wildcard, true), or( inArray(certificates.domain, [...domainLevelDownSet]), inArray(certificates.domain, [...wildcardDomainSet]) + ), + or( + isNotNull(orgDomains.orgId), + isNotNull(domainNamespaces.domainNamespaceId) ) ) ); diff --git a/src/hooks/useCertificate.ts b/src/hooks/useCertificate.ts index 1a4ff46f4..9afbb766e 100644 --- a/src/hooks/useCertificate.ts +++ b/src/hooks/useCertificate.ts @@ -111,7 +111,7 @@ export function useCertificate({ let certError: string | null = null; if (restartCert.isError) { certError = "Failed to restart"; - } else if (isError || initialCertValue === null) { + } else if (isError || (!isLoading && data === null)) { // Null value means failed to get the certificate certError = "Failed"; }