/*
RFC-0896: Shared helpers for custom domain and redirect commands — system config
resolution, DNS/route/redirect payload builders, and environment variable resolution.
- Do not implement command logic — that lives in the individual command files.
- Do not log, echo, or serialize secret values.
- RFC-0896: initial shared helpers — resolveCustomDomainConfig, buildApexDnsRecord, buildApexRoutePattern, buildWwwDnsRecord, buildRedirectRuleExpression, buildRedirectRuleDescription, resolveCustomDomainEnv.
*/
import type { SystemConfig } from "@warpgogol/werkstatt-engine/schemas";
import { readSystemConfig } from "../sternsystem/registry-io.ts";
import { filterEnv, sourceDotenv } from "../leitstand/adapters/cloudflare-workers.ts";
export interface CustomDomainConfig {
zoneId: string;
apexDomain: string;
workerName: string;
}
export async function resolveCustomDomainConfig(
workspaceRoot: string,
systemId: string,
): Promise {
const config: SystemConfig = await readSystemConfig(workspaceRoot, systemId);
if (!config.cloudflareZoneId) {
throw new Error(
`[customdomain] System '${systemId}' has no cloudflareZoneId in system-config.yaml. Add it to enable custom domain registration.`,
);
}
const deployment = config.deployment;
if (!deployment) {
throw new Error(
`[customdomain] System '${systemId}' has no deployment config in system-config.yaml.`,
);
}
const mainUrl = deployment.channels.main.url;
const apexDomain = extractDomainFromUrl(mainUrl);
if (!apexDomain) {
throw new Error(
`[customdomain] System '${systemId}' has invalid deployment.channels.main.url: '${mainUrl}'. Expected a valid URL with a hostname.`,
);
}
const workerName = deployment.channels.main.workerName;
if (!workerName) {
throw new Error(
`[customdomain] System '${systemId}' has empty deployment.channels.main.workerName in system-config.yaml.`,
);
}
return {
zoneId: config.cloudflareZoneId,
apexDomain,
workerName,
};
}
function extractDomainFromUrl(url: string): string | null {
try {
const parsed = new URL(url);
return parsed.hostname;
} catch {
return null;
}
}
export function buildApexDnsRecord(domain: string): {
type: "A";
name: string;
content: string;
proxied: true;
} {
return {
type: "A",
name: domain,
content: "192.0.2.1",
proxied: true,
};
}
export function buildApexRoutePattern(domain: string): string {
return `${domain}/*`;
}
export function buildWwwDnsRecord(apexDomain: string): {
type: "CNAME";
name: string;
content: string;
proxied: true;
} {
return {
type: "CNAME",
name: `www.${apexDomain}`,
content: apexDomain,
proxied: true,
};
}
export function buildWwwDomain(apexDomain: string): string {
return `www.${apexDomain}`;
}
export function buildRedirectRuleExpression(wwwDomain: string): string {
return `(http.host eq "${wwwDomain}")`;
}
export function buildRedirectRuleDescription(systemId: string): string {
return `www → apex 301 (${systemId})`;
}
export function buildRedirectRuleTargetExpression(apexDomain: string): string {
return `concat("https://${apexDomain}", http.request.uri.path)`;
}
export async function resolveCustomDomainEnv(
secretsFilePath?: string,
): Promise> {
const secretsEnv = secretsFilePath ? await sourceDotenv(secretsFilePath) : {};
const merged = { ...filterEnv(process.env), ...secretsEnv };
const token = merged["CLOUDFLARE_API_TOKEN"];
if (!token) {
throw new Error(
"[customdomain] CLOUDFLARE_API_TOKEN is required. The token must have " +
"Zone:DNS:Edit, Workers Routes:Edit, and Zone:Transform:Edit permissions.",
);
}
return merged;
}