chore: remove feature plan files after merge
This commit is contained in:
@@ -1,33 +0,0 @@
|
|||||||
# Feature Context: Cloudflare DNS Management
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
- **Development mode:** Automated
|
|
||||||
- **Execution mode:** Direct
|
|
||||||
- **Strategy:** Big Bang
|
|
||||||
- **Build (Go):** `go build ./cmd/server`
|
|
||||||
- **Build (Frontend):** `cd web && npm run build`
|
|
||||||
- **Check (Frontend):** `cd web && npm run check`
|
|
||||||
- **Test:** `go test ./...`
|
|
||||||
- **Dev server:** `./scripts/dev-server.sh` (port 8090)
|
|
||||||
|
|
||||||
## Current State
|
|
||||||
Starting fresh — no implementation yet.
|
|
||||||
|
|
||||||
## Cross-Phase Dependencies
|
|
||||||
- Phase 2 depends on Phase 1 (settings fields for Cloudflare credentials)
|
|
||||||
- Phase 3 depends on Phase 2 (dns.Provider interface)
|
|
||||||
- Phase 4 depends on Phase 1 (API endpoints for settings)
|
|
||||||
- Phase 5 depends on Phase 2 + Phase 6 (client + sync logic)
|
|
||||||
- Phase 6 depends on Phase 2 (Cloudflare client) + Phase 3 (dns_records table)
|
|
||||||
|
|
||||||
## Key Architecture Decisions
|
|
||||||
- DNS provider abstraction via `internal/dns.Provider` interface
|
|
||||||
- Cloudflare API v4 via direct HTTP (no SDK) — keeps dependencies minimal
|
|
||||||
- Local `dns_records` table tracks managed records for reconciliation
|
|
||||||
- DNS operations are best-effort (log warnings, don't block deploys)
|
|
||||||
- A records only, pointing to `ServerIP` from settings
|
|
||||||
|
|
||||||
## Environment & Runtime Notes
|
|
||||||
- Encryption key from `ENCRYPTION_KEY` env var (AES-256-GCM)
|
|
||||||
- SQLite with WAL mode, auto-migration on startup
|
|
||||||
- Frontend is SvelteKit 2 + Svelte 5 + Tailwind CSS 4
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Feature: Cloudflare DNS Management
|
|
||||||
|
|
||||||
**Branch:** `feature/cloudflare-dns-management`
|
|
||||||
**Base branch:** `main`
|
|
||||||
**Created:** 2026-04-02
|
|
||||||
**Status:** 🟡 In Progress
|
|
||||||
**Strategy:** Big Bang
|
|
||||||
**Mode:** Automated
|
|
||||||
**Execution:** Direct
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Introduce flexible DNS management. By default, wildcard DNS is assumed (current behavior).
|
|
||||||
When disabled, the user selects a DNS provider (Cloudflare initially) and provides API
|
|
||||||
credentials. DNS A records are then automatically kept in sync with proxy consumers
|
|
||||||
(deployed instances and standalone proxies). A dedicated DNS Records page provides
|
|
||||||
visibility, filtering, and manual sync/reconciliation.
|
|
||||||
|
|
||||||
## Build & Test Commands
|
|
||||||
- **Build (Go):** `go build ./cmd/server`
|
|
||||||
- **Build (Frontend):** `cd web && npm run build`
|
|
||||||
- **Check (Frontend):** `cd web && npm run check`
|
|
||||||
- **Test (Go):** `go test ./...`
|
|
||||||
- **Dev server:** `./scripts/dev-server.sh`
|
|
||||||
|
|
||||||
## Phases
|
|
||||||
|
|
||||||
- [ ] Phase 1: Settings model & API [domain: backend] → [subplan](./phase-1-settings-model.md)
|
|
||||||
- [ ] Phase 2: Cloudflare DNS client [domain: backend] → [subplan](./phase-2-cloudflare-client.md)
|
|
||||||
- [ ] Phase 3: DNS lifecycle hooks [domain: backend] → [subplan](./phase-3-dns-hooks.md)
|
|
||||||
- [ ] Phase 4: Settings UI — DNS configuration [domain: frontend] → [subplan](./phase-4-settings-ui.md)
|
|
||||||
- [ ] Phase 5: DNS Records page [domain: fullstack] → [subplan](./phase-5-dns-records-page.md)
|
|
||||||
- [ ] Phase 6: DNS sync & reconciliation [domain: backend] → [subplan](./phase-6-dns-sync.md)
|
|
||||||
|
|
||||||
## Phase Progress Log
|
|
||||||
|
|
||||||
| Phase | Domain | Status | Review | Build | Committed |
|
|
||||||
|-------|--------|--------|--------|-------|-----------|
|
|
||||||
| Phase 1: Settings model & API | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
| Phase 2: Cloudflare DNS client | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
| Phase 3: DNS lifecycle hooks | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
| Phase 4: Settings UI — DNS config | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
| Phase 5: DNS Records page | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
| Phase 6: DNS sync & reconciliation | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
|
||||||
|
|
||||||
## Final Review
|
|
||||||
- [ ] Comprehensive code review
|
|
||||||
- [ ] Full build passes
|
|
||||||
- [ ] Full test suite passes
|
|
||||||
- [ ] Merged to `main`
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# Phase 1: Settings Model & API
|
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
|
||||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
||||||
**Domain:** backend
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
Extend the Settings model and API to support DNS provider configuration.
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
- [ ] Task 1: Add new fields to `Settings` struct in `internal/store/models.go`
|
|
||||||
- `WildcardDNS` (bool, default true)
|
|
||||||
- `DNSProvider` (string, default "")
|
|
||||||
- `CloudflareAPIToken` (string, encrypted)
|
|
||||||
- `CloudflareZoneID` (string)
|
|
||||||
- [ ] Task 2: Add migration columns in `internal/store/store.go`
|
|
||||||
- `wildcard_dns` INTEGER DEFAULT 1
|
|
||||||
- `dns_provider` TEXT DEFAULT ''
|
|
||||||
- `cloudflare_api_token` TEXT DEFAULT ''
|
|
||||||
- `cloudflare_zone_id` TEXT DEFAULT ''
|
|
||||||
- [ ] Task 3: Update `GetSettings()` and `UpdateSettings()` in `internal/store/settings.go`
|
|
||||||
- Read/write new fields
|
|
||||||
- Encrypt/decrypt `cloudflare_api_token`
|
|
||||||
- [ ] Task 4: Update `GET /api/settings` handler to include new fields (mask token)
|
|
||||||
- [ ] Task 5: Update `PUT /api/settings` handler to accept new fields
|
|
||||||
- [ ] Task 6: Add `POST /api/settings/dns/test` endpoint — validate Cloudflare token + zone
|
|
||||||
- [ ] Task 7: Add `GET /api/settings/dns/zones` endpoint — list Cloudflare zones for picker
|
|
||||||
- [ ] Task 8: Register new routes in `internal/api/router.go`
|
|
||||||
|
|
||||||
## Files to Modify/Create
|
|
||||||
- `internal/store/models.go` — add fields to Settings struct
|
|
||||||
- `internal/store/store.go` — add migration columns
|
|
||||||
- `internal/store/settings.go` — update read/write queries
|
|
||||||
- `internal/api/settings.go` — update handlers, add new endpoints
|
|
||||||
- `internal/api/router.go` — register new routes
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
- New settings fields are persisted and retrievable
|
|
||||||
- Cloudflare API token is encrypted at rest
|
|
||||||
- GET /api/settings returns new fields (token masked)
|
|
||||||
- PUT /api/settings accepts and stores new fields
|
|
||||||
- DNS test and zones endpoints registered (can return placeholder until Phase 2)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Token encryption uses existing `crypto.Encrypt/Decrypt`
|
|
||||||
- `has_cloudflare_api_token` bool in GET response (same pattern as npm_password)
|
|
||||||
- DNS test/zones endpoints will make real Cloudflare API calls — Phase 2 client needed
|
|
||||||
for full implementation, but can use inline HTTP calls for these two endpoints
|
|
||||||
|
|
||||||
## 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 -->
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# Phase 2: Cloudflare DNS Client
|
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
|
||||||
**Parent plan:** [PLAN.md](./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 `Provider` interface in `internal/dns/provider.go`
|
|
||||||
- `EnsureRecord(ctx, fqdn, ip) error` — create or update A record
|
|
||||||
- `DeleteRecord(ctx, fqdn) error` — delete A record if exists
|
|
||||||
- `ListRecords(ctx) ([]Record, error)` — list all A records in the zone
|
|
||||||
- `Record` struct: 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 differs
|
|
||||||
- `DeleteRecord`: GET record by name, DELETE if found
|
|
||||||
- `ListRecords`: GET all A records in zone
|
|
||||||
- `ListZones`: GET zones for the token (for zone picker)
|
|
||||||
- `TestConnection`: verify token works (GET /user/tokens/verify)
|
|
||||||
- [ ] Task 3: Create `internal/dns/dns.go` — factory function
|
|
||||||
- `NewProvider(providerName, config) (Provider, error)`
|
|
||||||
- Config struct with token, zoneID
|
|
||||||
- Returns `nil, nil` when providerName is empty (wildcard mode)
|
|
||||||
- [ ] Task 4: Wire DNS test/zones endpoints in `internal/api/settings.go`
|
|
||||||
- `POST /api/settings/dns/test` — create temp Cloudflare client, call TestConnection
|
|
||||||
- `GET /api/settings/dns/zones` — create temp client, call ListZones
|
|
||||||
|
|
||||||
## Files to Modify/Create
|
|
||||||
- `internal/dns/provider.go` — interface + Record type
|
|
||||||
- `internal/dns/cloudflare.go` — Cloudflare implementation
|
|
||||||
- `internal/dns/dns.go` — factory function
|
|
||||||
- `internal/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.Context` for 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)
|
|
||||||
|
|
||||||
## Handoff to Next Phase
|
|
||||||
<!-- Filled in after completion -->
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
# 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 -->
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# Phase 4: Settings UI — DNS Configuration
|
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
|
||||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
||||||
**Domain:** frontend
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
Add a "DNS Configuration" section to the Settings page with wildcard toggle, provider
|
|
||||||
selection, Cloudflare credential fields, and connection test.
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
- [ ] Task 1: Add new API functions in `web/src/lib/api.ts`
|
|
||||||
- `testDnsConnection(token, zoneId)` → POST /api/settings/dns/test
|
|
||||||
- `listDnsZones(token)` → GET /api/settings/dns/zones
|
|
||||||
- [ ] Task 2: Add i18n keys for DNS settings in locale files
|
|
||||||
- [ ] Task 3: Add DNS Configuration section to `web/src/routes/settings/+page.svelte`
|
|
||||||
- Toggle: "Wildcard DNS is configured" (checkbox/switch)
|
|
||||||
- When unchecked, show:
|
|
||||||
- DNS Provider dropdown (only "Cloudflare" option)
|
|
||||||
- API Token field (password type, show `has_cloudflare_api_token` indicator)
|
|
||||||
- Zone picker (loaded from API after token provided)
|
|
||||||
- "Test Connection" button with success/error feedback
|
|
||||||
- All DNS fields hidden when wildcard is checked
|
|
||||||
- [ ] Task 4: Wire save logic — include new fields in `handleSave`
|
|
||||||
- [ ] Task 5: Wire load logic — populate DNS fields from settings response
|
|
||||||
|
|
||||||
## Files to Modify/Create
|
|
||||||
- `web/src/lib/api.ts` — add DNS API functions
|
|
||||||
- `web/src/routes/settings/+page.svelte` — add DNS config section
|
|
||||||
- `web/src/lib/i18n/en.ts` (or equivalent locale file) — add DNS translation keys
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
- Wildcard toggle visible and functional (default: checked)
|
|
||||||
- Unchecking reveals Cloudflare configuration form
|
|
||||||
- API token field uses password masking
|
|
||||||
- Zone picker loads zones from Cloudflare API
|
|
||||||
- Test Connection button shows success/failure
|
|
||||||
- Settings save includes DNS fields
|
|
||||||
- Settings load populates DNS fields
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Follow existing settings page patterns (FormField, EntityPicker for zones)
|
|
||||||
- Zone picker similar to SSL certificate picker pattern
|
|
||||||
- Token field similar to NPM password field (has_token indicator)
|
|
||||||
|
|
||||||
## 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 -->
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# Phase 5: DNS Records Page
|
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
|
||||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
||||||
**Domain:** fullstack
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
Create a dedicated DNS Records page that lists all managed DNS records with filtering,
|
|
||||||
consumer mapping, and sync status visibility.
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
- [ ] Task 1: Add backend endpoint `GET /api/dns/records`
|
|
||||||
- Returns merged view: local dns_records + Cloudflare actual records
|
|
||||||
- Each record: fqdn, type, value (IP), consumer_type, consumer_name, status (synced/orphaned/missing)
|
|
||||||
- Orphaned = exists in Cloudflare but no local consumer
|
|
||||||
- Missing = local consumer exists but no Cloudflare record
|
|
||||||
- [ ] Task 2: Add API handler in `internal/api/dns.go`
|
|
||||||
- New handler file for DNS-related endpoints
|
|
||||||
- Register routes in router.go
|
|
||||||
- [ ] Task 3: Add frontend API function `getDnsRecords()` in `api.ts`
|
|
||||||
- [ ] Task 4: Create DNS Records page at `web/src/routes/dns/+page.svelte`
|
|
||||||
- Table with columns: FQDN, Type, Value, Consumer, Status
|
|
||||||
- Consumer column shows: instance name (project/stage) or standalone proxy name
|
|
||||||
- Status badges: synced (green), orphaned (yellow), missing (red)
|
|
||||||
- Search filter (by FQDN substring)
|
|
||||||
- Filter by consumer type: all / managed / standalone
|
|
||||||
- Filter by status: all / synced / orphaned / missing
|
|
||||||
- Manual sync button (calls POST /api/dns/sync — Phase 6)
|
|
||||||
- Refresh button to re-fetch from Cloudflare
|
|
||||||
- [ ] Task 5: Add navigation link to DNS page
|
|
||||||
- Only visible when wildcard DNS is disabled
|
|
||||||
- Add to sidebar/nav under Settings or as top-level
|
|
||||||
- [ ] Task 6: Add i18n keys for DNS records page
|
|
||||||
|
|
||||||
## Files to Modify/Create
|
|
||||||
- `internal/api/dns.go` — new handler file
|
|
||||||
- `internal/api/router.go` — register DNS routes
|
|
||||||
- `web/src/lib/api.ts` — add DNS records API function
|
|
||||||
- `web/src/routes/dns/+page.svelte` — new page
|
|
||||||
- `web/src/routes/dns/+page.ts` — optional load function
|
|
||||||
- Navigation component — add DNS link
|
|
||||||
- Locale files — add i18n keys
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
- DNS Records page accessible at /dns
|
|
||||||
- Table shows all records with correct status
|
|
||||||
- Filtering works: search text, consumer type, sync status
|
|
||||||
- Only accessible/visible when wildcard DNS is disabled
|
|
||||||
- Consumer names resolve correctly (project/stage for managed, proxy name for standalone)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Status computation: compare local dns_records table with Cloudflare ListRecords response
|
|
||||||
- Cache Cloudflare response for a few seconds to avoid rate limiting on page load
|
|
||||||
- Navigation link visibility tied to settings (may need a store or settings check)
|
|
||||||
|
|
||||||
## 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 -->
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
# Phase 6: DNS Sync & Reconciliation
|
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
|
||||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
||||||
**Domain:** backend
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
Implement reconciliation logic that compares expected DNS records (from active consumers)
|
|
||||||
with actual Cloudflare records, and provides a sync endpoint to fix discrepancies.
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
- [ ] Task 1: Add `POST /api/dns/sync` endpoint
|
|
||||||
- Computes expected records from: active instances with proxy + standalone proxies
|
|
||||||
- Fetches actual records from Cloudflare via ListRecords
|
|
||||||
- Creates missing records (consumer exists, no CF record)
|
|
||||||
- Deletes orphaned records (CF record exists, no consumer) — only for records in dns_records table
|
|
||||||
- Updates dns_records table to reflect current state
|
|
||||||
- Returns sync report: created N, deleted N, already_synced N
|
|
||||||
- [ ] Task 2: Add helper to compute expected records
|
|
||||||
- Query all instances where npm_proxy_id > 0 and status = "running" → extract FQDN
|
|
||||||
- Query all standalone proxies → extract domain
|
|
||||||
- Return list of expected FQDNs
|
|
||||||
- [ ] Task 3: Add `DELETE /api/dns/records/{fqdn}` endpoint
|
|
||||||
- Manual deletion of a specific DNS record (for orphan cleanup)
|
|
||||||
- Calls provider.DeleteRecord + removes from dns_records
|
|
||||||
- [ ] Task 4: Wire sync endpoint in `internal/api/dns.go` and router
|
|
||||||
- [ ] Task 5: Add frontend sync button handler in DNS Records page
|
|
||||||
- Call POST /api/dns/sync
|
|
||||||
- Show sync report (toast or inline)
|
|
||||||
- Refresh records list after sync
|
|
||||||
|
|
||||||
## Files to Modify/Create
|
|
||||||
- `internal/api/dns.go` — add sync + delete endpoints
|
|
||||||
- `internal/api/router.go` — register new routes
|
|
||||||
- `internal/store/dns_records.go` — add helper queries (list consumers with FQDNs)
|
|
||||||
- `web/src/lib/api.ts` — add syncDnsRecords(), deleteDnsRecord() functions
|
|
||||||
- `web/src/routes/dns/+page.svelte` — wire sync button
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
- POST /api/dns/sync creates missing and removes orphaned records
|
|
||||||
- Sync report returned with counts
|
|
||||||
- Manual delete endpoint works for individual records
|
|
||||||
- Frontend sync button triggers reconciliation and refreshes view
|
|
||||||
- Only records tracked in dns_records table are candidates for orphan deletion
|
|
||||||
(don't delete unrelated Cloudflare records)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Safety: only delete Cloudflare records that are tracked in our dns_records table
|
|
||||||
(never touch records we didn't create)
|
|
||||||
- Rate limiting: Cloudflare API has rate limits, batch operations where possible
|
|
||||||
- Expected records query needs to join instances + standalone_proxies with settings.domain
|
|
||||||
|
|
||||||
## 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 -->
|
|
||||||
Reference in New Issue
Block a user