Files
tiny-forge/plans/cloudflare-dns-management/phase-3-dns-hooks.md
T
alexei.dolgolyov c730cfaa45 feat: Cloudflare DNS management with automatic record sync
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
2026-04-02 14:49:21 +03:00

68 lines
3.1 KiB
Markdown

# Phase 3: DNS Lifecycle Hooks
**Status:** ⬜ Not Started
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** backend
## Objective
Hook DNS record management into the deployer and standalone proxy manager so that DNS
records are automatically created/updated/deleted in sync with proxy consumers.
## Tasks
- [ ] Task 1: Create `dns_records` table for tracking managed records
- Columns: id, fqdn, provider_record_id, consumer_type (instance/standalone), consumer_id, created_at, updated_at
- Store queries: CreateDNSRecord, DeleteDNSRecord, GetDNSRecordByFQDN, ListDNSRecords, GetDNSRecordsByConsumer
- [ ] Task 2: Add DNS provider to `Deployer` struct
- Accept `dns.Provider` in constructor (can be nil for wildcard mode)
- Helper: `ensureDNS(ctx, fqdn, deployID)` — calls provider.EnsureRecord + saves to dns_records
- Helper: `removeDNS(ctx, fqdn, deployID)` — calls provider.DeleteRecord + removes from dns_records
- [ ] Task 3: Hook into deployer — instance creation
- After `configureProxy` succeeds in `deployer.go` and `bluegreen.go` → call `ensureDNS`
- FQDN = `subdomain + "." + settings.Domain`
- [ ] Task 4: Hook into deployer — instance removal
- In `removeInstance` after NPM proxy deletion → call `removeDNS`
- In `rollback` after NPM proxy deletion → call `removeDNS`
- [ ] Task 5: Hook into standalone proxy manager
- `CreateProxy` → after NPM host created, call `ensureDNS`
- `UpdateProxy` → if domain changed, `removeDNS(old)` + `ensureDNS(new)`
- `DeleteProxy` → call `removeDNS`
- [ ] Task 6: Wire DNS provider into main.go
- Read settings on startup, create provider if non-wildcard
- Pass provider to Deployer and proxy Manager constructors
- Handle provider being nil (wildcard mode = no DNS ops)
- [ ] Task 7: Add `DNSRecord` model to `internal/store/models.go`
## Files to Modify/Create
- `internal/store/models.go` — add DNSRecord struct
- `internal/store/store.go` — add dns_records table migration
- `internal/store/dns_records.go` — CRUD queries
- `internal/deployer/deployer.go` — add DNS hooks
- `internal/deployer/bluegreen.go` — add DNS hooks
- `internal/deployer/rollback.go` — add DNS cleanup
- `internal/proxy/manager.go` — add DNS hooks
- `cmd/server/main.go` — wire DNS provider
## Acceptance Criteria
- DNS records created when proxy consumers are created (if non-wildcard mode)
- DNS records deleted when proxy consumers are removed
- DNS records updated when standalone proxy domain changes
- All DNS operations are best-effort (log warning on failure, don't block)
- dns_records table tracks all managed records
- Wildcard mode (default) skips all DNS operations
## Notes
- DNS operations must be wrapped in error handling that logs but doesn't fail the deploy
- The dns_records table is the local source of truth for reconciliation (Phase 6)
- Provider can be nil — all hooks must check for nil before calling
## Review Checklist
- [ ] All tasks completed
- [ ] Code follows project conventions
- [ ] No unintended side effects
- [ ] Build passes
- [ ] Tests pass (new + existing)
## Handoff to Next Phase
<!-- Filled in after completion -->