/** * Create the custom-domain DNS records at the owner's DNS provider. * * POST /_emdash/api/settings/custom-domain/dns * { domain, provider: "cloudflare" | "simply", credentials: { ... } } * GET /_emdash/api/settings/custom-domain/dns?domain=… * → the provider catalogue, plus which one the domain's nameservers point at * * The credentials live in this one request: they are used to write the * records the hosting platform asks for (fetched fresh from it) and are * neither stored nor logged. The caller re-checks the domain afterwards. */ import type { APIRoute } from "astro"; import { z } from "zod"; import { requirePerm } from "#api/authorize.js"; import { apiError, apiSuccess, handleError } from "#api/error.js"; import { isParseError, parseBody } from "#api/parse.js"; import { NotManagedError, platformCustomDomain } from "../../../../../dns/platform.js"; import { applyDnsRecords, detectDnsProvider, DnsProviderError, listDnsProviders, } from "../../../../../dns/providers.js"; export const prerender = false; const BodySchema = z.object({ domain: z.string().trim().min(1).max(253), provider: z.string().trim().min(1).max(40), credentials: z.record(z.string(), z.string().max(4096)), }); export const GET: APIRoute = async ({ locals, url }) => { const denied = requirePerm(locals.user, "settings:manage"); if (denied) return denied; const domain = (url.searchParams.get("domain") ?? "").trim().slice(0, 253); const detected = domain ? await detectDnsProvider(domain) : { provider: null, nameservers: [] }; return apiSuccess({ providers: listDnsProviders(), detected: detected.provider, nameservers: detected.nameservers }); }; export const POST: APIRoute = async ({ locals, request }) => { const { emdash, user } = locals; if (!emdash?.db) return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500); const denied = requirePerm(user, "settings:manage"); if (denied) return denied; const body = await parseBody(request, BodySchema); if (isParseError(body)) return body; try { const check = await platformCustomDomain(emdash.db, { domain: body.domain, action: "check" }); if (!check.success) return apiError("CUSTOM_DOMAIN_ERROR", check.error ?? "Check failed", 502); if (check.active) return apiSuccess({ alreadyActive: true }); const records = (check.records ?? []).map((r) => ({ type: r.type, name: r.name, value: r.value })); const applied = await applyDnsRecords(body.provider, body.credentials, records); return apiSuccess(applied); } catch (error) { if (error instanceof NotManagedError) return apiError("NOT_MANAGED", error.message, 400); if (error instanceof DnsProviderError) return apiError("DNS_PROVIDER_ERROR", error.message, 400); return handleError(error, "Could not add the DNS records", "DNS_SETUP_ERROR"); } };