fix(dns): a lista dizia PENDING, nunca verificado, com tudo verificado

O crocha na lista de dominios lia-se da coluna dns_status, e nada lhe escrevia
depois do onboarding. A verificacao corre no servico de protocolos, que tem o
resolvedor, e a resposta ia directa para o browser sem ficar registada -- por
isso um dominio com 13 de 13 registos publicados e a resolver continuava
listado como PENDING, ultima verificacao "Nunca", ao lado da pagina que dizia
o contrario.

Quem verificou passa a escrever o que encontrou. Falhar a gravar nao
transforma uma verificacao boa num erro para o operador: fica no log.

A coluna aceita apenas PENDING, VERIFIED, PARTIAL, DRIFT e ERROR, e o
verificador tambem diz MISSING e UNKNOWN. Escrever qualquer uma dessas violava
a check constraint, a actualizacao era rejeitada e o crocha ficava velo na
mesma -- exactamente o sintoma que isto vem corrigir. Ha um teste so para essa
traducao.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5 committed 2026-08-24 08:47:38 -03:00
1 parent 47c3700380
commit 9689f26dda
6 files changed
+139

No files matched your search

@@ -47,6 +47,15 @@ type DomainReconciler interface {
ReconcileDomain(ctx context.Context, domain string) (ReconcileResult, error)
}
// DnsStatusWriter records the outcome of a verification.
//
// Verification lives here, with the resolver; the badge the console shows is
// read from the domains table. Without this the two never met: a domain could
// verify 13 of 13 and still be listed as PENDING, never checked.
type DnsStatusWriter interface {
SaveDnsStatus(ctx context.Context, domain, status string) error
}
type adminDnsAPI struct {
spec DnsSpecSource
verifier DnsRecordVerifier
@@ -54,6 +63,9 @@ type adminDnsAPI struct {
// reconciler is optional: without it the route reports that the feature
// is not configured rather than answering as though it had run.
reconciler DomainReconciler
// status is optional: without it verification still answers, it just is
// not written down.
status DnsStatusWriter
}
// handleReconcile publishes the records a domain is missing.
@@ -168,6 +180,15 @@ func (a *adminDnsAPI) handleVerify(w http.ResponseWriter, r *http.Request) {
status = "PARTIAL"
}
// Written down before answering, so the list badge and this page agree.
if a.status != nil {
if err := a.status.SaveDnsStatus(r.Context(), domain, status); err != nil {
// The check itself succeeded; failing to file it must not turn a
// good answer into an error for the operator.
log.Printf("dns: verified %s but could not record the status: %v", domain, err)
}
}
writeJSON(w, http.StatusOK, map[string]any{
"domain": domain,
"status": status,
@@ -189,6 +210,7 @@ func (r *Router) SetAdminDnsAPI(spec DnsSpecSource, verifier DnsRecordVerifier,
// either order. They could not before, and main.go called them in the
// order that lost the reconciler.
reconciler: r.dnsReconciler,
status: r.dnsStatus,
}
}
@@ -208,6 +230,14 @@ func (r *Router) handleAdminDnsReconcile(w http.ResponseWriter, req *http.Reques
// SetDnsReconciler enables the reconcile route to actually publish records.
// Without it the route reports that reconciliation is not configured.
// SetDnsStatusWriter lets verification record what it found.
func (r *Router) SetDnsStatusWriter(w DnsStatusWriter) {
r.dnsStatus = w
if r.dns != nil {
r.dns.status = w
}
}
func (r *Router) SetDnsReconciler(reconciler DomainReconciler) {
// Kept on the router as well as on the API. Assigning only to r.dns meant
// that calling this before SetAdminDnsAPI silently did nothing: the field