c730cfaa45
Add flexible DNS management to Docker Watcher. By default, wildcard DNS is assumed (current behavior). When disabled, users can configure a Cloudflare DNS provider with API token and zone selection. DNS A records are automatically created/updated/deleted in sync with proxy consumers (deployed instances and standalone proxies). - Settings: wildcard_dns toggle, dns_provider, cloudflare credentials - Cloudflare client: Provider interface with EnsureRecord/DeleteRecord/ListRecords - DNS lifecycle hooks in deployer and proxy manager (best-effort) - Settings UI: DNS config section with provider picker, zone selector, test button - DNS Records page at /dns with filtering, sync status, reconciliation - Records visible in both wildcard and managed modes - Cleanup on provider change: removes old records when switching modes
2.6 KiB
2.6 KiB
Phase 2: Cloudflare DNS Client
Status: ⬜ Not Started Parent plan: PLAN.md Domain: backend
Objective
Create an internal/dns package with a Provider interface and a Cloudflare implementation
using the Cloudflare API v4 (direct HTTP, no SDK).
Tasks
- Task 1: Define
Providerinterface ininternal/dns/provider.goEnsureRecord(ctx, fqdn, ip) error— create or update A recordDeleteRecord(ctx, fqdn) error— delete A record if existsListRecords(ctx) ([]Record, error)— list all A records in the zoneRecordstruct: ID, FQDN, Type, Content (IP), Proxied, TTL
- Task 2: Create
internal/dns/cloudflare.go— Cloudflare implementation- HTTP client with
Authorization: Bearer <token>header - Base URL:
https://api.cloudflare.com/client/v4 EnsureRecord: GET records by name, create if missing, update if IP differsDeleteRecord: GET record by name, DELETE if foundListRecords: GET all A records in zoneListZones: GET zones for the token (for zone picker)TestConnection: verify token works (GET /user/tokens/verify)
- HTTP client with
- Task 3: Create
internal/dns/dns.go— factory functionNewProvider(providerName, config) (Provider, error)- Config struct with token, zoneID
- Returns
nil, nilwhen providerName is empty (wildcard mode)
- Task 4: Wire DNS test/zones endpoints in
internal/api/settings.goPOST /api/settings/dns/test— create temp Cloudflare client, call TestConnectionGET /api/settings/dns/zones— create temp client, call ListZones
Files to Modify/Create
internal/dns/provider.go— interface + Record typeinternal/dns/cloudflare.go— Cloudflare implementationinternal/dns/dns.go— factory functioninternal/api/settings.go— wire test/zones endpoints to real client
Acceptance Criteria
- Provider interface defined with EnsureRecord, DeleteRecord, ListRecords
- Cloudflare client makes correct API calls with proper auth headers
- EnsureRecord is idempotent (create if missing, update if changed, no-op if same)
- DeleteRecord is idempotent (no error if record doesn't exist)
- ListZones returns zone ID + name pairs
- TestConnection returns success/failure
Notes
- Cloudflare API v4 docs: zones endpoint, dns_records endpoint
- Use
context.Contextfor timeout control on all HTTP calls - A records only (type "A"), TTL=1 (auto), proxied=false (DNS only, not CF proxy)
Review Checklist
- All tasks completed
- Code follows project conventions
- No unintended side effects
- Build passes
- Tests pass (new + existing)