feat(api): read zone records without template + snapshot-to-template
LoadDomain requires a template, so a zone without one could never be
viewed or snapshotted. Adds a template-free path: store.LoadZone /
service.ZoneRef / DomainService.ZoneRecords read a zone's live records
straight from the provider (no diff, no template). GET
/domains/{did}/records exposes read-only viewing; POST
/domains/{did}/template-from-zone snapshots only managed record types
(NS/SOA excluded) into a new template and auto-attaches it to the domain.
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:
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/crypto"
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/diff"
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/model"
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/provider"
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/provider/registry"
|
||||
"github.com/vasyakrg/dns-autoresolver/internal/store/dto"
|
||||
@@ -20,8 +21,17 @@ type DomainRef struct {
|
||||
Template dto.TemplateDoc
|
||||
}
|
||||
|
||||
// ZoneRef is the provider-access subset of a domain, without a template —
|
||||
// enough to read a zone's live records.
|
||||
type ZoneRef struct {
|
||||
ZoneID string
|
||||
Provider string
|
||||
SecretEnc string
|
||||
}
|
||||
|
||||
type Loader interface {
|
||||
LoadDomain(ctx context.Context, projectID, domainID uuid.UUID) (DomainRef, error)
|
||||
LoadZone(ctx context.Context, projectID, domainID uuid.UUID) (ZoneRef, error)
|
||||
}
|
||||
|
||||
type Recorder interface {
|
||||
@@ -81,6 +91,25 @@ func (s *DomainService) Check(ctx context.Context, projectID, domainID uuid.UUID
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// ZoneRecords reads a zone's current records straight from the provider,
|
||||
// with no diff and no template required. Used for read-only zone viewing and
|
||||
// as the source for a snapshot template.
|
||||
func (s *DomainService) ZoneRecords(ctx context.Context, projectID, domainID uuid.UUID) ([]model.Record, error) {
|
||||
ref, err := s.loader.LoadZone(ctx, projectID, domainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := s.reg.ByName(ref.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := s.cipher.Decrypt(ref.SecretEnc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.GetRecords(ctx, provider.Credentials{Secret: string(secret)}, ref.ZoneID)
|
||||
}
|
||||
|
||||
// Apply applies updates always (when ApplyUpdates) and prunes only when ApplyPrunes.
|
||||
func (s *DomainService) Apply(ctx context.Context, projectID, domainID uuid.UUID, req ApplyRequest) (diff.Changeset, error) {
|
||||
p, creds, ref, cs, err := s.resolve(ctx, projectID, domainID)
|
||||
|
||||
Reference in New Issue
Block a user