feat: add DNS provider integration (Cloudflare, AWS Route53)

Lets you connect a DNS provider and manage its records (create,
update, delete) from Settings -> DNS Providers, instead of doing it
by hand in Cloudflare/AWS.

- dns_provider table, org-scoped, jsonb config as a discriminated
  union per provider type
- Cloudflare adapter (REST, bearer token)
- Route53 adapter (AWS SDK, SigV4); records are identified by
  type:name since Route53 has no native record id, so update
  handles renames as delete-old + upsert-new
- listZones/listRecords/createRecord/updateRecord/deleteRecord/
  testConnection wired through a shared DnsClient interface
- Settings UI: provider management, zone browser, record CRUD,
  IP-fill dropdown for A records (panel IP + remote servers)
- Access control: dnsProvider resource wired into custom roles
- Unit tests for both adapters and the config mask/merge logic
This commit is contained in:
Mauricio Siu
2026-08-12 03:25:25 -06:00
parent 3848fa9c0d
commit 4d58f3987f
29 changed files with 12233 additions and 18 deletions
@@ -49,6 +49,7 @@ export const statements = {
monitoring: ["read"],
auditLog: ["read"],
vaultProvider: ["read", "create", "update", "delete"],
dnsProvider: ["read", "create", "update", "delete"],
} as const;
/**
@@ -76,6 +77,7 @@ export const enterpriseOnlyResources = new Set<string>([
"monitoring",
"auditLog",
"vaultProvider",
"dnsProvider",
]);
export const ac = createAccessControl(statements);
@@ -116,6 +118,7 @@ export const ownerRole = ac.newRole({
monitoring: ["read"],
auditLog: ["read"],
vaultProvider: ["read", "create", "update", "delete"],
dnsProvider: ["read", "create", "update", "delete"],
});
/**
@@ -154,6 +157,7 @@ export const adminRole = ac.newRole({
monitoring: ["read"],
auditLog: ["read"],
vaultProvider: ["read", "create", "update", "delete"],
dnsProvider: ["read", "create", "update", "delete"],
});
/**
@@ -198,4 +202,6 @@ export const memberRole = ac.newRole({
auditLog: [],
// Members need provider/secret names for env editor autocomplete; values are never exposed
vaultProvider: ["read"],
// Members can see configured DNS providers to pick one when adding a domain
dnsProvider: ["read"],
});