UI: per-field DNS record copy + manual recheck button

closed
#4611274 opened by agent Sep 11

Problem

The domain DNS page (internal/appview/ui/domain.templ) shows each record as one pre-formatted zonefile string (name TYPE content) with a single Copy button copying the whole line. Users configuring DNS through a web form (type dropdown + separate name/content fields) must hand-split the string. There is also no way to trigger a recheck — users wait blindly on the 5-minute background verifier.

Proposal

  • Replace DNSView’s three preformatted strings with a []DNSRecordView{Type, Name, Content} list (structured data already exists via getDnsState / dnsprober.expectedRecords; only the UI flattens it).
  • Render a table: Type badge | Name (click-to-copy) | Content (click-to-copy, DKIM truncated with expand). Reuse the generic data-copy JS helper in static/app.js.
  • Add POST /domains/{id}/recheck: build a dnsprober.Target from store
    • mail defaults, call prober.Verify, persist the status, redirect with a flash (active vs still verifying — checked just now, auto-rechecks every 5m).

Files

  • internal/appview/ui/view.go (DNSView), handler.go (buildDNS), domain.templ, static/app.js (only if helper needs work)
  • Tests: extend ui_test.go (render + recheck handler with stub prober)

Acceptance

  • Each Type/Name/Content field copies individually on click.
  • Recheck button reports live state without waiting for the background sweep.

2 Comments

agent 4a621d1 Sep 11

Addendum: collapse DNS when active + last-verified (bundled scope)

Once a domain is active, keep listing the records is confusing. Extend this issue:

  • Add verified_at to the domain table in the fresh schema (decided 2026-09-11: no ALTER migration — dev and smoke tables get blown away and redeployed; get the table design right instead of maintaining backwards compat). Update it in verifier sweepOne and in the manual recheck handler from this issue.
  • domain.templ: when Status == active, collapse records into <details> showing “Active — last verified
  • Note: live-testing this needs a fresh test environment because of the table schema change.
agent 476e131 Sep 11

Implementation complete (all in working copy, uncommitted)

Subagent-driven execution of the plan, full suite SOVRN_INTEGRATION=0 go test -count=1 ./... green.

Store (store.go, sqlite.go): Domain.VerifiedAt *time.Time; mail_domains.verified_at TEXT nullable in fresh DDL only (no ALTER); UpdateDomainStatus(ctx,id,status,verifiedAt) writes both atomically; all SELECT/INSERT/SCAN round-trip it. sqlite_test.go covers nil→value→nil.

Verifier + provision: sweepOne stamps s.now().UTC() on activation; ProvisionDomain seeds VerifiedAt when state==active. Unready path untouched. Tests assert non-nil on activate, nil when still verifying.

DNSView (view.go, handler.go buildDNS): []DNSRecordView{Type,Name,Content} — MX/SPF/DKIM with rdata-only Content. domain.templ: unverified = full Type|Name|Content table (Name/Content each data-copy, FULL values, JS-disabled selectable); active = collapsed <details> “Active — last verified

Recheck (handler.go): POST /domains/{id}/recheck — Verify from store+mail defaults, domain.Enable on verifying→active only (nil-Stalwart guard for tests), UpdateDomainStatus with fresh now on EVERY active result (so re-rechecking active bumps last-verified), 303 ?msg=active vs ?msg=still verifying — checked just now, auto-rechecks every 5m. No demotion on still-verifying (matches verifier semantics). Tests: activates, still-verifying, active-bumps-timestamp.

Dev DB: data/dev/sovrn.db* deleted (old schema confirmed lacking the column); fresh store.Open verified to create verified_at + round-trip. Next just dev boot recreates it. If orphaned Stalwart domains collide, run sovrn-reset.

Known nits (accepted): flash q() helper leaves em-dash unescaped in Location (pre-existing pattern); still-verifying persists nothing by design.