Add cusotmizable cname target domain

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-10-01 09:07:32 +02:00
parent e33bc760d5
commit 2865b484bc
7 changed files with 112 additions and 21 deletions

View File

@@ -17,6 +17,7 @@ package probod
type customDomainsConfig struct { type customDomainsConfig struct {
RenewalInterval int `json:"renewal-interval"` RenewalInterval int `json:"renewal-interval"`
ProvisionInterval int `json:"provision-interval"` ProvisionInterval int `json:"provision-interval"`
CnameTarget string `json:"cname-target"`
ACME acmeConfig `json:"acme"` ACME acmeConfig `json:"acme"`
} }

View File

@@ -309,9 +309,8 @@ func (impl *Implm) Run(
ConnectorRegistry: defaultConnectorRegistry, ConnectorRegistry: defaultConnectorRegistry,
Agent: agent, Agent: agent,
SafeRedirect: &saferedirect.SafeRedirect{AllowedHost: impl.cfg.Hostname}, SafeRedirect: &saferedirect.SafeRedirect{AllowedHost: impl.cfg.Hostname},
CustomDomainCname: impl.cfg.CustomDomains.CnameTarget,
Logger: l.Named("http.server"), Logger: l.Named("http.server"),
PgClient: pgClient,
EncryptionKey: impl.cfg.EncryptionKey,
Auth: api.ConsoleAuthConfig{ Auth: api.ConsoleAuthConfig{
CookieName: impl.cfg.Auth.Cookie.Name, CookieName: impl.cfg.Auth.Cookie.Name,
CookieDomain: impl.cfg.Auth.Cookie.Domain, CookieDomain: impl.cfg.Auth.Cookie.Domain,

View File

@@ -53,15 +53,16 @@ type (
} }
Config struct { Config struct {
AllowedOrigins []string AllowedOrigins []string
Probo *probo.Service Probo *probo.Service
Usrmgr *usrmgr.Service Usrmgr *usrmgr.Service
Trust *trust.Service Trust *trust.Service
Auth ConsoleAuthConfig Auth ConsoleAuthConfig
TrustAuth TrustAuthConfig TrustAuth TrustAuthConfig
ConnectorRegistry *connector.ConnectorRegistry ConnectorRegistry *connector.ConnectorRegistry
SafeRedirect *saferedirect.SafeRedirect SafeRedirect *saferedirect.SafeRedirect
Logger *log.Logger CustomDomainCname string
Logger *log.Logger
} }
Server struct { Server struct {
@@ -154,6 +155,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}, },
s.cfg.ConnectorRegistry, s.cfg.ConnectorRegistry,
s.cfg.SafeRedirect, s.cfg.SafeRedirect,
s.cfg.CustomDomainCname,
), ),
) )

View File

@@ -53,9 +53,10 @@ type (
} }
Resolver struct { Resolver struct {
proboSvc *probo.Service proboSvc *probo.Service
usrmgrSvc *usrmgr.Service usrmgrSvc *usrmgr.Service
authCfg AuthConfig authCfg AuthConfig
customDomainCname string
} }
ctxKey struct{ name string } ctxKey struct{ name string }
@@ -84,6 +85,7 @@ func NewMux(
authCfg AuthConfig, authCfg AuthConfig,
connectorRegistry *connector.ConnectorRegistry, connectorRegistry *connector.ConnectorRegistry,
safeRedirect *saferedirect.SafeRedirect, safeRedirect *saferedirect.SafeRedirect,
customDomainCname string,
) *chi.Mux { ) *chi.Mux {
r := chi.NewMux() r := chi.NewMux()
@@ -204,20 +206,21 @@ func NewMux(
})) }))
r.Get("/", playground.Handler("GraphQL", "/api/console/v1/query")) r.Get("/", playground.Handler("GraphQL", "/api/console/v1/query"))
r.Post("/query", graphqlHandler(logger, proboSvc, usrmgrSvc, authCfg)) r.Post("/query", graphqlHandler(logger, proboSvc, usrmgrSvc, authCfg, customDomainCname))
return r return r
} }
func graphqlHandler(logger *log.Logger, proboSvc *probo.Service, usrmgrSvc *usrmgr.Service, authCfg AuthConfig) http.HandlerFunc { func graphqlHandler(logger *log.Logger, proboSvc *probo.Service, usrmgrSvc *usrmgr.Service, authCfg AuthConfig, customDomainCname string) http.HandlerFunc {
var mb int64 = 1 << 20 var mb int64 = 1 << 20
es := schema.NewExecutableSchema( es := schema.NewExecutableSchema(
schema.Config{ schema.Config{
Resolvers: &Resolver{ Resolvers: &Resolver{
proboSvc: proboSvc, proboSvc: proboSvc,
usrmgrSvc: usrmgrSvc, usrmgrSvc: usrmgrSvc,
authCfg: authCfg, authCfg: authCfg,
customDomainCname: customDomainCname,
}, },
}, },
) )

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
)
func NewCustomDomainConnection(
p *page.Page[*coredata.CustomDomain, coredata.CustomDomainOrderField],
parentType any,
parentID gid.GID,
cnameTarget string,
) *CustomDomainConnection {
var edges = make([]*CustomDomainEdge, len(p.Data))
for i := range edges {
edges[i] = NewCustomDomainEdge(p.Data[i], p.Cursor.OrderBy.Field, cnameTarget)
}
return &CustomDomainConnection{
Edges: edges,
PageInfo: NewPageInfo(p),
TotalCount: len(p.Data),
}
}
func NewCustomDomainEdge(
d *coredata.CustomDomain,
orderBy coredata.CustomDomainOrderField,
cnameTarget string,
) *CustomDomainEdge {
return &CustomDomainEdge{
Cursor: d.CursorKey(orderBy),
Node: NewCustomDomain(d, cnameTarget),
}
}
func NewCustomDomain(d *coredata.CustomDomain, cnameTarget string) *CustomDomain {
result := &CustomDomain{
ID: d.ID,
Domain: d.Domain,
IsActive: d.IsActive,
SslStatus: d.SSLStatus,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
SslExpiresAt: d.SSLExpiresAt,
}
// Convert DNS records
result.DNSRecords = convertDNSRecords(d, cnameTarget)
return result
}
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",
}
records = append(records, record)
return records
}

View File

@@ -3325,7 +3325,7 @@ func (r *mutationResolver) CreateCustomDomain(ctx context.Context, input types.C
} }
edge := &types.CustomDomainEdge{ edge := &types.CustomDomainEdge{
Node: types.NewCustomDomain(domain), Node: types.NewCustomDomain(domain, r.customDomainCname),
Cursor: page.NewCursorKey(domain.ID, domain.CreatedAt), Cursor: page.NewCursorKey(domain.ID, domain.CreatedAt),
} }
@@ -4032,7 +4032,7 @@ func (r *organizationResolver) CustomDomains(ctx context.Context, obj *types.Org
return nil, fmt.Errorf("failed to list custom domains: %w", err) return nil, fmt.Errorf("failed to list custom domains: %w", err)
} }
return types.NewCustomDomainConnection(page, r, obj.ID), nil return types.NewCustomDomainConnection(page, r, obj.ID, r.customDomainCname), nil
} }
// TotalCount is the resolver for the totalCount field. // TotalCount is the resolver for the totalCount field.

View File

@@ -44,6 +44,7 @@ type Config struct {
ConnectorRegistry *connector.ConnectorRegistry ConnectorRegistry *connector.ConnectorRegistry
Agent *agents.Agent Agent *agents.Agent
SafeRedirect *saferedirect.SafeRedirect SafeRedirect *saferedirect.SafeRedirect
CustomDomainCname string
Logger *log.Logger Logger *log.Logger
} }
@@ -68,6 +69,7 @@ func NewServer(cfg Config) (*Server, error) {
TrustAuth: cfg.TrustAuth, TrustAuth: cfg.TrustAuth,
ConnectorRegistry: cfg.ConnectorRegistry, ConnectorRegistry: cfg.ConnectorRegistry,
SafeRedirect: cfg.SafeRedirect, SafeRedirect: cfg.SafeRedirect,
CustomDomainCname: cfg.CustomDomainCname,
Logger: cfg.Logger.Named("api"), Logger: cfg.Logger.Named("api"),
} }
apiServer, err := api.NewServer(apiCfg) apiServer, err := api.NewServer(apiCfg)