From caeb6d922588b22dfb321b6223d506f0ff69c32f Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Fri, 17 Jul 2026 17:39:33 +0200 Subject: [PATCH] Resolve custom domain SSL via certificate Replace flattened SSL fields with a certificate relation loaded through certmanager, and resolve domain slots from IDs already on the trust center instead of reloading the compliance page. Signed-off-by: Bryan Frimin --- .../_components/CompliancePageDomainCard.tsx | 18 +++-- .../CompliancePageDomainDialog.tsx | 27 ++++---- .../NewCompliancePageDomainDialog.tsx | 7 +- .../management/domain_service.go | 65 ++----------------- .../console/v1/graphql/trust_center.graphql | 35 +++++----- pkg/server/api/console/v1/resolver.go | 20 ------ .../api/console/v1/trust_center_resolvers.go | 63 +++++++++++++----- .../api/console/v1/types/certificate.go | 34 ++++++++++ .../api/console/v1/types/custom_domain.go | 48 +++++--------- .../api/console/v1/types/trust_center.go | 10 +++ pkg/server/api/mcp/v1/schema.resolvers.go | 11 +++- pkg/server/api/mcp/v1/specification.yaml | 30 +++++++-- pkg/server/api/mcp/v1/types/custom_domain.go | 16 ++--- 13 files changed, 198 insertions(+), 186 deletions(-) create mode 100644 pkg/server/api/console/v1/types/certificate.go diff --git a/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainCard.tsx b/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainCard.tsx index ef6dbc5a7..95f76df5b 100644 --- a/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainCard.tsx +++ b/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainCard.tsx @@ -37,8 +37,10 @@ const fragment = graphql` id domain managed - sslStatus - provisioningError + certificate { + status + provisioningError + } canDelete: permission(action: "compliance-portal:custom-domain:delete") ...CompliancePageDomainDialogFragment } @@ -52,6 +54,8 @@ export function CompliancePageDomainCard(props: { const { __ } = useTranslate(); const domain = useFragment(fragment, fKey); + const sslStatus = domain.certificate?.status ?? "PENDING"; + const provisioningError = domain.certificate?.provisioningError; return ( @@ -62,15 +66,15 @@ export function CompliancePageDomainCard(props: { {domain.managed && ( {__("Managed")} )} - - {getCustomDomainStatusBadgeLabel(domain.sslStatus, __)} + + {getCustomDomainStatusBadgeLabel(sslStatus, __)}

- {domain.sslStatus === "ACTIVE" + {sslStatus === "ACTIVE" ? __("Verified and serving traffic") - : domain.provisioningError - ? domain.provisioningError + : provisioningError + ? provisioningError : __("Pending DNS verification")}

diff --git a/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainDialog.tsx b/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainDialog.tsx index 365b3a69d..ab262ac00 100644 --- a/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainDialog.tsx +++ b/apps/console/src/pages/organizations/compliance-page/domain/_components/CompliancePageDomainDialog.tsx @@ -39,9 +39,12 @@ import type { CompliancePageDomainDialogFragment$key } from "#/__generated__/cor const fragment = graphql` fragment CompliancePageDomainDialogFragment on CustomDomain { - sslStatus domain - provisioningError + certificate { + status + expiresAt + provisioningError + } dnsRecords { type name @@ -49,7 +52,6 @@ const fragment = graphql` ttl purpose } - sslExpiresAt } `; @@ -72,6 +74,9 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp }; const domain = useFragment(fragment, fKey); + const sslStatus = domain.certificate?.status ?? "PENDING"; + const expiresAt = domain.certificate?.expiresAt; + const provisioningError = domain.certificate?.provisioningError; return ( {domain.domain} - - {getCustomDomainStatusBadgeLabel(domain.sslStatus, __)} + + {getCustomDomainStatusBadgeLabel(sslStatus, __)} )} > - {domain.sslStatus === "ACTIVE" + {sslStatus === "ACTIVE" ? (
@@ -109,11 +114,11 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp "Your custom domain is verified and SSL certificate is active", )}

- {domain.sslExpiresAt && ( + {expiresAt && (

{__("SSL expires")} {" "} - {new Date(domain.sslExpiresAt).toLocaleDateString()} + {new Date(expiresAt).toLocaleDateString()}

)}
@@ -122,10 +127,10 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp ) : (
- {domain.provisioningError && ( + {provisioningError && (

{__("Provisioning error")}

-

{domain.provisioningError}

+

{provisioningError}

)} @@ -188,7 +193,7 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp ))}
- {domain.sslStatus === "PENDING" && ( + {sslStatus === "PENDING" && (

{__( diff --git a/apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx b/apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx index 29301e01d..5dfc76643 100644 --- a/apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx +++ b/apps/console/src/pages/organizations/compliance-page/domain/_components/NewCompliancePageDomainDialog.tsx @@ -42,7 +42,11 @@ const createCustomDomainMutation = graphql` customDomain { id domain - sslStatus + certificate { + status + expiresAt + provisioningError + } dnsRecords { type name @@ -52,7 +56,6 @@ const createCustomDomainMutation = graphql` } createdAt updatedAt - sslExpiresAt canDelete: permission(action: "compliance-portal:custom-domain:delete") ...CompliancePageDomainCardFragment } diff --git a/pkg/complianceportal/management/domain_service.go b/pkg/complianceportal/management/domain_service.go index b5b79a49c..d2fbb6726 100644 --- a/pkg/complianceportal/management/domain_service.go +++ b/pkg/complianceportal/management/domain_service.go @@ -138,73 +138,18 @@ func (s *Service) RemoveCustomDomain( ) } -// GetDefaultDomain returns the compliance page's default probopage subdomain, -// or nil when it has not been provisioned yet. -func (s *Service) GetDefaultDomain( +// GetDomain returns a custom domain by ID. +func (s *Service) GetDomain( ctx context.Context, scope coredata.Scoper, - compliancePageID gid.GID, + domainID gid.GID, ) (*coredata.CustomDomain, error) { - var domain *coredata.CustomDomain + domain := &coredata.CustomDomain{} err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - compliancePage := &coredata.TrustCenter{} - if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load compliance page: %w", err) - } - - if compliancePage.DefaultDomainID == nil { - return nil - } - - domain = &coredata.CustomDomain{} - if err := domain.LoadByID(ctx, conn, scope, *compliancePage.DefaultDomainID); err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - domain = nil - return nil - } - - return fmt.Errorf("cannot load custom domain: %w", err) - } - - return nil - }, - ) - if err != nil { - return nil, err - } - - return domain, nil -} - -func (s *Service) GetCustomDomain( - ctx context.Context, - scope coredata.Scoper, - compliancePageID gid.GID, -) (*coredata.CustomDomain, error) { - var domain *coredata.CustomDomain - - err := s.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - compliancePage := &coredata.TrustCenter{} - if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load compliance page: %w", err) - } - - if compliancePage.CustomDomainID == nil { - return nil - } - - domain = &coredata.CustomDomain{} - if err := domain.LoadByID(ctx, conn, scope, *compliancePage.CustomDomainID); err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - domain = nil - return nil - } - + if err := domain.LoadByID(ctx, conn, scope, domainID); err != nil { return fmt.Errorf("cannot load custom domain: %w", err) } diff --git a/pkg/server/api/console/v1/graphql/trust_center.graphql b/pkg/server/api/console/v1/graphql/trust_center.graphql index f3b51be40..a35793639 100644 --- a/pkg/server/api/console/v1/graphql/trust_center.graphql +++ b/pkg/server/api/console/v1/graphql/trust_center.graphql @@ -249,31 +249,21 @@ enum TrustCenterFileOrderField } enum SSLStatus - @goModel(model: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatus") { + @goModel(model: "go.probo.inc/probo/pkg/coredata.CertificateStatus") { PENDING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusPending" - ) + @goEnum(value: "go.probo.inc/probo/pkg/coredata.CertificateStatusPending") PROVISIONING @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusProvisioning" + value: "go.probo.inc/probo/pkg/coredata.CertificateStatusProvisioning" ) ACTIVE - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusActive" - ) + @goEnum(value: "go.probo.inc/probo/pkg/coredata.CertificateStatusActive") RENEWING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusRenewing" - ) + @goEnum(value: "go.probo.inc/probo/pkg/coredata.CertificateStatusRenewing") EXPIRED - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusExpired" - ) + @goEnum(value: "go.probo.inc/probo/pkg/coredata.CertificateStatusExpired") FAILED - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusFailed" - ) + @goEnum(value: "go.probo.inc/probo/pkg/coredata.CertificateStatusFailed") } input TrustCenterAccessOrder @@ -648,9 +638,7 @@ type CustomDomain implements Node { organization: Organization! domain: String! managed: Boolean! - sslStatus: SSLStatus! - sslExpiresAt: Datetime - provisioningError: String + certificate: Certificate @goField(forceResolver: true) dnsRecords: [DNSRecordInstruction!]! createdAt: Datetime! updatedAt: Datetime! @@ -658,6 +646,13 @@ type CustomDomain implements Node { permission(action: String!): Boolean! @goField(forceResolver: true) } +type Certificate { + id: ID! + status: SSLStatus! + expiresAt: Datetime + provisioningError: String +} + type DNSRecordInstruction { type: String! name: String! diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index 3524b1249..76d83e9d1 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -85,26 +85,6 @@ type ( } ) -// newCustomDomainType loads the domain's certificate (when present) and builds -// the GraphQL CustomDomain type with its certificate-backed SSL fields. -func (r *Resolver) newCustomDomainType( - ctx context.Context, - scope coredata.Scoper, - domain *coredata.CustomDomain, -) (*types.CustomDomain, error) { - var cert *coredata.Certificate - if domain != nil && domain.CertificateID != nil { - var err error - cert, err = r.certManager.Get(ctx, scope, *domain.CertificateID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot load certificate", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - return types.NewCustomDomain(domain, cert, r.customDomainCname), nil -} - func NewMux( logger *log.Logger, proboSvc *probo.Service, diff --git a/pkg/server/api/console/v1/trust_center_resolvers.go b/pkg/server/api/console/v1/trust_center_resolvers.go index 8a9e67568..54fb430bf 100644 --- a/pkg/server/api/console/v1/trust_center_resolvers.go +++ b/pkg/server/api/console/v1/trust_center_resolvers.go @@ -124,6 +124,30 @@ func (r *compliancePortalCommitmentGroupConnectionResolver) TotalCount(ctx conte return count, nil } +// Certificate is the resolver for the certificate field. +func (r *customDomainResolver) Certificate(ctx context.Context, obj *types.CustomDomain) (*types.Certificate, error) { + if obj.Certificate == nil { + return nil, nil + } + + scope, err := r.authorize(ctx, obj.ID, management.ActionCustomDomainGet) + if err != nil { + return nil, err + } + + cert, err := r.certManager.Get(ctx, scope, obj.Certificate.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil + } + + r.logger.ErrorCtx(ctx, "cannot load certificate", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewCertificate(cert), nil +} + // Permission is the resolver for the permission field. func (r *customDomainResolver) Permission(ctx context.Context, obj *types.CustomDomain, action string) (bool, error) { return r.Resolver.Permission(ctx, obj, action) @@ -893,13 +917,8 @@ func (r *mutationResolver) CreateCustomDomain(ctx context.Context, input types.C return nil, gqlutils.Internal(ctx) } - customDomain, err := r.newCustomDomainType(ctx, scope, domain) - if err != nil { - return nil, err - } - return &types.CreateCustomDomainPayload{ - CustomDomain: customDomain, + CustomDomain: types.NewCustomDomain(domain, r.customDomainCname), }, nil } @@ -1169,42 +1188,50 @@ func (r *trustCenterResolver) MailingList(ctx context.Context, obj *types.TrustC // DefaultDomain is the resolver for the defaultDomain field. func (r *trustCenterResolver) DefaultDomain(ctx context.Context, obj *types.TrustCenter) (*types.CustomDomain, error) { + if obj.DefaultDomain == nil { + return nil, nil + } + scope, err := r.authorize(ctx, obj.ID, management.ActionCustomDomainGet) if err != nil { return nil, err } - domain, err := r.management.GetDefaultDomain(ctx, scope, obj.ID) + domain, err := r.management.GetDomain(ctx, scope, obj.DefaultDomain.ID) if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil + } + r.logger.ErrorCtx(ctx, "cannot load default domain", log.Error(err)) return nil, gqlutils.Internal(ctx) } - if domain == nil { - return nil, nil - } - - return r.newCustomDomainType(ctx, scope, domain) + return types.NewCustomDomain(domain, r.customDomainCname), nil } // CustomDomain is the resolver for the customDomain field. func (r *trustCenterResolver) CustomDomain(ctx context.Context, obj *types.TrustCenter) (*types.CustomDomain, error) { + if obj.CustomDomain == nil { + return nil, nil + } + scope, err := r.authorize(ctx, obj.ID, management.ActionCustomDomainGet) if err != nil { return nil, err } - domain, err := r.management.GetCustomDomain(ctx, scope, obj.ID) + domain, err := r.management.GetDomain(ctx, scope, obj.CustomDomain.ID) if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil + } + r.logger.ErrorCtx(ctx, "cannot load custom domain", log.Error(err)) return nil, gqlutils.Internal(ctx) } - if domain == nil { - return nil, nil - } - - return r.newCustomDomainType(ctx, scope, domain) + return types.NewCustomDomain(domain, r.customDomainCname), nil } // PublicURL is the resolver for the publicUrl field. diff --git a/pkg/server/api/console/v1/types/certificate.go b/pkg/server/api/console/v1/types/certificate.go new file mode 100644 index 000000000..cfbe4e0cf --- /dev/null +++ b/pkg/server/api/console/v1/types/certificate.go @@ -0,0 +1,34 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package types + +import ( + "go.probo.inc/probo/pkg/coredata" +) + +func NewCertificate(c *coredata.Certificate) *Certificate { + return &Certificate{ + ID: c.ID, + Status: c.Status, + ExpiresAt: c.SSLExpiresAt, + ProvisioningError: c.ProvisioningError, + } +} diff --git a/pkg/server/api/console/v1/types/custom_domain.go b/pkg/server/api/console/v1/types/custom_domain.go index 366dc0fe9..dbf5cd075 100644 --- a/pkg/server/api/console/v1/types/custom_domain.go +++ b/pkg/server/api/console/v1/types/custom_domain.go @@ -24,46 +24,34 @@ import ( "go.probo.inc/probo/pkg/coredata" ) -// NewCustomDomain builds the GraphQL CustomDomain type. The TLS lifecycle now -// lives on the linked certificate; when cert is nil (certificate not yet -// created) the domain reports a pending SSL status. -func NewCustomDomain(d *coredata.CustomDomain, cert *coredata.Certificate, cnameTarget string) *CustomDomain { - result := &CustomDomain{ +func NewCustomDomain(d *coredata.CustomDomain, cnameTarget string) *CustomDomain { + domain := &CustomDomain{ ID: d.ID, Organization: &Organization{ ID: d.OrganizationID, }, - Domain: d.Domain, - Managed: d.Managed, - SslStatus: coredata.CustomDomainSSLStatusPending, - CreatedAt: d.CreatedAt, - UpdatedAt: d.UpdatedAt, + Domain: d.Domain, + Managed: d.Managed, + DNSRecords: convertDNSRecords(d, cnameTarget), + CreatedAt: d.CreatedAt, + UpdatedAt: d.UpdatedAt, } - if cert != nil { - result.SslStatus = coredata.CustomDomainSSLStatus(cert.Status) - result.SslExpiresAt = cert.SSLExpiresAt - result.ProvisioningError = cert.ProvisioningError + if d.CertificateID != nil { + domain.Certificate = &Certificate{ID: *d.CertificateID} } - // Convert DNS records - result.DNSRecords = convertDNSRecords(d, cnameTarget) - - return result + return domain } func convertDNSRecords(d *coredata.CustomDomain, cnameTarget string) []*DNSRecordInstruction { - var records []*DNSRecordInstruction - - // For HTTP-01 challenges, we just need the domain to point to our servers via CNAME - record := &DNSRecordInstruction{ - Type: "CNAME", - Name: d.Domain, - Value: cnameTarget, - TTL: 300, - Purpose: "Point domain to Probo servers", + return []*DNSRecordInstruction{ + { + Type: "CNAME", + Name: d.Domain, + Value: cnameTarget, + TTL: 300, + Purpose: "Point domain to Probo servers", + }, } - records = append(records, record) - - return records } diff --git a/pkg/server/api/console/v1/types/trust_center.go b/pkg/server/api/console/v1/types/trust_center.go index 2953aaf16..05c44dcd6 100644 --- a/pkg/server/api/console/v1/types/trust_center.go +++ b/pkg/server/api/console/v1/types/trust_center.go @@ -47,6 +47,8 @@ type TrustCenter struct { ComplianceFrameworks *ComplianceFrameworkConnection `json:"complianceFrameworks"` CustomLinks *ComplianceCustomLinkConnection `json:"customLinks"` MailingList *MailingList `json:"mailingList,omitempty"` + DefaultDomain *CustomDomain `json:"defaultDomain,omitempty"` + CustomDomain *CustomDomain `json:"customDomain,omitempty"` Permission bool `json:"permission"` } @@ -82,5 +84,13 @@ func NewTrustCenter(tc *coredata.TrustCenter) *TrustCenter { trustCenter.Nda = &File{ID: *tc.NonDisclosureAgreementFileID} } + if tc.DefaultDomainID != nil { + trustCenter.DefaultDomain = &CustomDomain{ID: *tc.DefaultDomainID} + } + + if tc.CustomDomainID != nil { + trustCenter.CustomDomain = &CustomDomain{ID: *tc.CustomDomainID} + } + return trustCenter } diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index 03a62e38b..d316a15ac 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -5306,15 +5306,20 @@ func (r *Resolver) DeleteCustomDomainTool(ctx context.Context, req *mcp.CallTool return nil, types.DeleteCustomDomainOutput{}, err } - domain, err := r.management.GetCustomDomain(ctx, scope, input.TrustCenterID) + compliancePage, err := r.management.Get(ctx, scope, input.TrustCenterID) if err != nil { - return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("cannot get custom domain: %w", err) + return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("cannot load compliance page: %w", err) } - if domain == nil { + if compliancePage.CustomDomainID == nil { return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("compliance page has no custom domain") } + domain, err := r.management.GetDomain(ctx, scope, *compliancePage.CustomDomainID) + if err != nil { + return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("cannot get custom domain: %w", err) + } + var cert *coredata.Certificate if domain.CertificateID != nil { cert, err = r.certManager.Get(ctx, scope, *domain.CertificateID) diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index e9e514a3c..6fcd0b33e 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -8737,7 +8737,7 @@ components: - RENEWING - EXPIRED - FAILED - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatus + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.CertificateStatus TrustCenterReferenceOrderField: type: string @@ -8968,6 +8968,26 @@ components: type: string format: date-time + Certificate: + type: object + required: + - id + - status + properties: + id: + $ref: "#/components/schemas/GID" + status: + $ref: "#/components/schemas/SSLStatus" + expires_at: + anyOf: + - type: string + format: date-time + - type: "null" + provisioning_error: + anyOf: + - type: string + - type: "null" + CustomDomain: type: object required: @@ -8975,7 +8995,6 @@ components: - organization_id - domain - managed - - ssl_status - created_at - updated_at properties: @@ -8988,12 +9007,9 @@ components: managed: type: boolean description: Whether this domain is a Probo-managed probopage subdomain - ssl_status: - $ref: "#/components/schemas/SSLStatus" - ssl_expires_at: + certificate: anyOf: - - type: string - format: date-time + - $ref: "#/components/schemas/Certificate" - type: "null" created_at: type: string diff --git a/pkg/server/api/mcp/v1/types/custom_domain.go b/pkg/server/api/mcp/v1/types/custom_domain.go index a2b9375cf..be61949c3 100644 --- a/pkg/server/api/mcp/v1/types/custom_domain.go +++ b/pkg/server/api/mcp/v1/types/custom_domain.go @@ -24,24 +24,24 @@ import ( "go.probo.inc/probo/pkg/coredata" ) -// NewCustomDomain builds the MCP CustomDomain type. The TLS lifecycle now lives -// on the linked certificate; when cert is nil (certificate not yet created) the -// domain reports a pending SSL status. func NewCustomDomain(d *coredata.CustomDomain, cert *coredata.Certificate) *CustomDomain { - result := &CustomDomain{ + domain := &CustomDomain{ ID: d.ID, OrganizationID: d.OrganizationID, Domain: d.Domain, Managed: d.Managed, - SslStatus: coredata.CustomDomainSSLStatusPending, CreatedAt: d.CreatedAt, UpdatedAt: d.UpdatedAt, } if cert != nil { - result.SslStatus = coredata.CustomDomainSSLStatus(cert.Status) - result.SslExpiresAt = cert.SSLExpiresAt + domain.Certificate = &Certificate{ + ID: cert.ID, + Status: cert.Status, + ExpiresAt: cert.SSLExpiresAt, + ProvisioningError: cert.ProvisioningError, + } } - return result + return domain }