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 <bryan@probo.com>
This commit is contained in:
@@ -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<CompliancePageDomainCardFragment$key>(fragment, fKey);
|
||||
const sslStatus = domain.certificate?.status ?? "PENDING";
|
||||
const provisioningError = domain.certificate?.provisioningError;
|
||||
|
||||
return (
|
||||
<Card padded>
|
||||
@@ -62,15 +66,15 @@ export function CompliancePageDomainCard(props: {
|
||||
{domain.managed && (
|
||||
<Badge variant="neutral">{__("Managed")}</Badge>
|
||||
)}
|
||||
<Badge variant={getCustomDomainStatusBadgeVariant(domain.sslStatus)}>
|
||||
{getCustomDomainStatusBadgeLabel(domain.sslStatus, __)}
|
||||
<Badge variant={getCustomDomainStatusBadgeVariant(sslStatus)}>
|
||||
{getCustomDomainStatusBadgeLabel(sslStatus, __)}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-txt-secondary">
|
||||
{domain.sslStatus === "ACTIVE"
|
||||
{sslStatus === "ACTIVE"
|
||||
? __("Verified and serving traffic")
|
||||
: domain.provisioningError
|
||||
? domain.provisioningError
|
||||
: provisioningError
|
||||
? provisioningError
|
||||
: __("Pending DNS verification")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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<CompliancePageDomainDialogFragment$key>(fragment, fKey);
|
||||
const sslStatus = domain.certificate?.status ?? "PENDING";
|
||||
const expiresAt = domain.certificate?.expiresAt;
|
||||
const provisioningError = domain.certificate?.provisioningError;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -80,14 +85,14 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp
|
||||
title={(
|
||||
<div className="flex items-center gap-3">
|
||||
<span>{domain.domain}</span>
|
||||
<Badge variant={getCustomDomainStatusBadgeVariant(domain.sslStatus)}>
|
||||
{getCustomDomainStatusBadgeLabel(domain.sslStatus, __)}
|
||||
<Badge variant={getCustomDomainStatusBadgeVariant(sslStatus)}>
|
||||
{getCustomDomainStatusBadgeLabel(sslStatus, __)}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<DialogContent padded className="space-y-6">
|
||||
{domain.sslStatus === "ACTIVE"
|
||||
{sslStatus === "ACTIVE"
|
||||
? (
|
||||
<div className="bg-subtle rounded-lg p-4">
|
||||
<div className="flex items-start">
|
||||
@@ -109,11 +114,11 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp
|
||||
"Your custom domain is verified and SSL certificate is active",
|
||||
)}
|
||||
</p>
|
||||
{domain.sslExpiresAt && (
|
||||
{expiresAt && (
|
||||
<p className="text-xs text-txt-tertiary mt-2">
|
||||
{__("SSL expires")}
|
||||
{" "}
|
||||
{new Date(domain.sslExpiresAt).toLocaleDateString()}
|
||||
{new Date(expiresAt).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -122,10 +127,10 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp
|
||||
)
|
||||
: (
|
||||
<div>
|
||||
{domain.provisioningError && (
|
||||
{provisioningError && (
|
||||
<div className="bg-danger-subtle text-danger rounded-lg p-4 mb-4">
|
||||
<p className="text-sm font-medium mb-1">{__("Provisioning error")}</p>
|
||||
<p className="text-sm">{domain.provisioningError}</p>
|
||||
<p className="text-sm">{provisioningError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -188,7 +193,7 @@ export function CompliancePageDomainDialog(props: CompliancePageDomainDialogProp
|
||||
))}
|
||||
</div>
|
||||
|
||||
{domain.sslStatus === "PENDING" && (
|
||||
{sslStatus === "PENDING" && (
|
||||
<div className="bg-subtle rounded-lg p-4 mt-4">
|
||||
<p className="text-sm">
|
||||
{__(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
34
pkg/server/api/console/v1/types/certificate.go
Normal file
34
pkg/server/api/console/v1/types/certificate.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user