/*
RFC-0752: subdomain.register command handler — registers DNS CNAME and Workers
route for a service subdomain via the Cloudflare API. Idempotent: skips existing
correct records, errors on mismatched records.
- Do not auto-update mismatched DNS records or Workers routes — operator must fix manually.
- Do not handle DNS propagation waiting — that is out of scope.
- RFC-0752: initial subdomain.register command handler.
- RFC-1249: wire werkstatt.commands.validate into packages.check + retire CMD-OUTPUT debt
*/
import type {
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-engine/kernel";
import { readServicesRegistry, discoverSystems } from "../sternsystem/registry-io.ts";
import {
listDnsRecords,
createDnsRecord,
listWorkersRoutes,
createWorkersRoute,
} from "../leitstand/adapters/cloudflare-api.ts";
import {
flagString,
resolveService,
resolveZoneId,
resolveAccountSubdomain,
resolveSubdomainEnv,
buildCnameContent,
buildRoutePattern,
} from "./subdomain-helpers.ts";
export interface SubdomainRecord {
domain: string;
zone: string;
}
export interface SubdomainRegisterResult {
command: "subdomain.register";
serviceId: string;
subdomain: SubdomainRecord;
dnsRecord: {
id: string;
type: "CNAME";
name: string;
content: string;
proxied: boolean;
created: boolean;
};
workersRoute: {
id: string;
pattern: string;
script: string;
created: boolean;
};
state: "registered" | "already-registered" | "failed";
}
export async function runSubdomainRegister(
input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const { workspaceRoot } = context;
const serviceId = flagString(input, "service");
if (!serviceId) throw new Error("[subdomain.register] --service is required");
const registry = await readServicesRegistry(workspaceRoot);
const service = resolveService(registry, serviceId);
if (service.subdomains.length === 0) {
throw new Error(
`[subdomain.register] Service '${serviceId}' has no subdomains declared in the registry.`,
);
}
const env = await resolveSubdomainEnv();
const apiToken = env["CLOUDFLARE_API_TOKEN"];
if (!apiToken) {
throw new Error(
"[subdomain.register] CLOUDFLARE_API_TOKEN is not set. " +
"Set it in the environment or .env file. " +
"The token must have Zone:DNS:Edit and Workers Routes:Edit permissions.",
);
}
const account = resolveAccountSubdomain(service, env);
const subdomain = service.subdomains[0];
const { systems } = await discoverSystems(workspaceRoot);
const zoneId = resolveZoneId(systems, subdomain.zone);
const expectedCnameContent = buildCnameContent(service.workerName, account);
const expectedRoutePattern = buildRoutePattern(subdomain.domain);
const existingDnsRecords = await listDnsRecords(zoneId, apiToken, subdomain.domain);
const matchingDns = existingDnsRecords.find((r) => r.name === subdomain.domain);
let dnsResult: SubdomainRegisterResult["dnsRecord"];
let dnsCreated = false;
if (matchingDns) {
if (
matchingDns.type === "CNAME" &&
matchingDns.content === expectedCnameContent &&
matchingDns.proxied === true
) {
dnsResult = {
id: matchingDns.id,
type: "CNAME",
name: matchingDns.name,
content: matchingDns.content,
proxied: matchingDns.proxied,
created: false,
};
} else {
throw new Error(
`[subdomain.register] DNS record for '${subdomain.domain}' exists but has wrong values. ` +
`Current: type=${matchingDns.type}, content=${matchingDns.content}, proxied=${matchingDns.proxied}. ` +
`Expected: type=CNAME, content=${expectedCnameContent}, proxied=true. ` +
`Delete or fix the record manually before re-running.`,
);
}
} else {
const created = await createDnsRecord(zoneId, apiToken, {
type: "CNAME",
name: subdomain.domain,
content: expectedCnameContent,
proxied: true,
});
dnsCreated = true;
dnsResult = {
id: created.id,
type: "CNAME",
name: created.name,
content: created.content,
proxied: created.proxied,
created: true,
};
}
const existingRoutes = await listWorkersRoutes(zoneId, apiToken);
const matchingRoute = existingRoutes.find((r) => r.pattern === expectedRoutePattern);
let routeResult: SubdomainRegisterResult["workersRoute"];
let routeCreated = false;
if (matchingRoute) {
if (matchingRoute.script === service.workerName) {
routeResult = {
id: matchingRoute.id,
pattern: matchingRoute.pattern,
script: matchingRoute.script ?? service.workerName,
created: false,
};
} else {
throw new Error(
`[subdomain.register] Workers route for '${expectedRoutePattern}' exists but points to wrong script. ` +
`Current: script=${matchingRoute.script}. Expected: script=${service.workerName}. ` +
`Delete or fix the route manually before re-running.`,
);
}
} else {
const created = await createWorkersRoute(zoneId, apiToken, {
pattern: expectedRoutePattern,
script: service.workerName,
});
routeCreated = true;
routeResult = {
id: created.id,
pattern: created.pattern,
script: created.script ?? service.workerName,
created: true,
};
}
const state: SubdomainRegisterResult["state"] =
dnsCreated || routeCreated ? "registered" : "already-registered";
return {
data: {
command: "subdomain.register",
serviceId,
subdomain,
dnsRecord: dnsResult,
workersRoute: routeResult,
state,
},
exitCode: 0,
summary: `[subdomain.register] ${subdomain.domain}: ${state} (dns: ${dnsCreated ? "created" : "exists"}, route: ${routeCreated ? "created" : "exists"})`,
nextSteps: [],
};
}