fix(api): distinguish domain-not-found (404) from provider failure (502) on zone endpoints

Introduce service.ErrProviderUnavailable, wrapped only around the
provider GetRecords call in ZoneRecords. handleZoneRecords and
handleTemplateFromZone now use errors.Is against it to tell a real
provider outage (502) apart from local resolution failures such as an
unknown domain (404), instead of collapsing every ZoneRecords error
into a blanket 502. Also fixes handleTemplateFromZone's GetDomain
error branch to return 404 "domain not found" instead of 500, for
consistency with handleSetDomainTemplate/handleDomainHistory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
2026-07-05 12:14:46 +07:00
parent 9ccb304d2e
commit 5662334799
3 changed files with 53 additions and 7 deletions
+17 -1
View File
@@ -2,6 +2,8 @@ package service
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
@@ -13,6 +15,13 @@ import (
"github.com/vasyakrg/dns-autoresolver/internal/store/dto"
)
// ErrProviderUnavailable marks failures that happened while talking to the
// DNS provider itself (network, auth, rate limit, ...), as opposed to
// failures resolving the domain/zone locally (not found, bad credentials
// stored, unknown provider name). Callers use errors.Is against this to
// pick 502 vs 404 without leaking provider error details as "not found".
var ErrProviderUnavailable = errors.New("service: provider unavailable")
// DomainRef is the minimal data the service needs about a domain.
type DomainRef struct {
ZoneID string
@@ -107,7 +116,14 @@ func (s *DomainService) ZoneRecords(ctx context.Context, projectID, domainID uui
if err != nil {
return nil, err
}
return p.GetRecords(ctx, provider.Credentials{Secret: string(secret)}, ref.ZoneID)
recs, err := p.GetRecords(ctx, provider.Credentials{Secret: string(secret)}, ref.ZoneID)
if err != nil {
// Only a failure of the provider call itself is "provider unavailable" —
// LoadZone/ByName/Decrypt errors above are local resolution failures
// (e.g. domain not found) and must not be conflated with it.
return nil, fmt.Errorf("%w: %v", ErrProviderUnavailable, err)
}
return recs, nil
}
// Apply applies updates always (when ApplyUpdates) and prunes only when ApplyPrunes.