package api import ( "context" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/google/uuid" "github.com/vasyakrg/dns-autoresolver/internal/diff" "github.com/vasyakrg/dns-autoresolver/internal/provider" "github.com/vasyakrg/dns-autoresolver/internal/service" "github.com/vasyakrg/dns-autoresolver/internal/store" "github.com/vasyakrg/dns-autoresolver/internal/store/dto" ) // CheckApplier is the service surface the API depends on. type CheckApplier interface { Check(ctx context.Context, domainID uuid.UUID) (diff.Changeset, error) Apply(ctx context.Context, domainID uuid.UUID, req service.ApplyRequest) (diff.Changeset, error) } // TenantStore is the narrow persistence surface the CRUD handlers depend on. // *store.Store satisfies it directly via its thin wrapper methods (see // internal/store/tenant.go); tests can supply their own mock. type TenantStore interface { CreateAccount(ctx context.Context, projectID uuid.UUID, provider, secretEnc, comment string) (store.Account, error) ListAccounts(ctx context.Context, projectID uuid.UUID) ([]store.Account, error) GetAccount(ctx context.Context, id, projectID uuid.UUID) (store.Account, error) DeleteAccount(ctx context.Context, id, projectID uuid.UUID) error CreateTemplate(ctx context.Context, projectID uuid.UUID, name string, doc dto.TemplateDoc) (store.Template, error) ListTemplates(ctx context.Context, projectID uuid.UUID) ([]store.Template, error) UpdateTemplate(ctx context.Context, id, projectID uuid.UUID, name string, doc dto.TemplateDoc) (store.Template, error) DeleteTemplate(ctx context.Context, id, projectID uuid.UUID) error GetTemplate(ctx context.Context, id, projectID uuid.UUID) (store.Template, error) CreateDomain(ctx context.Context, projectID, accountID uuid.UUID, zoneName, zoneID string, templateID *uuid.UUID) (store.Domain, error) ListDomains(ctx context.Context, projectID uuid.UUID) ([]store.Domain, error) DeleteDomain(ctx context.Context, id, projectID uuid.UUID) error ImportDomains(ctx context.Context, projectID, accountID uuid.UUID, zones []provider.Zone) ([]store.Domain, error) SetDomainTemplate(ctx context.Context, domainID, projectID uuid.UUID, templateID *uuid.UUID) (store.Domain, error) } // Cipher encrypts/decrypts provider account secrets. *crypto.Cipher satisfies it. type Cipher interface { Encrypt(plaintext []byte) (string, error) Decrypt(enc string) ([]byte, error) } // ProviderRegistry resolves a provider.Provider by name. *registry.Registry satisfies it. type ProviderRegistry interface { ByName(name string) (provider.Provider, error) } // API holds handler dependencies. type API struct { Svc CheckApplier Store TenantStore Cipher Cipher Reg ProviderRegistry } func NewRouter(a *API) http.Handler { r := chi.NewRouter() r.Use(middleware.RequestID) r.Use(middleware.Recoverer) r.Route("/api/v1/projects/{pid}", func(r chi.Router) { r.Route("/domains", func(r chi.Router) { r.Post("/", a.handleCreateDomain) r.Get("/", a.handleListDomains) r.Route("/{did}", func(r chi.Router) { r.Get("/check", a.handleCheck) r.Post("/apply", a.handleApply) r.Patch("/", a.handleSetDomainTemplate) r.Delete("/", a.handleDeleteDomain) }) }) r.Route("/accounts", func(r chi.Router) { r.Post("/", a.handleCreateAccount) r.Get("/", a.handleListAccounts) r.Route("/{aid}", func(r chi.Router) { r.Delete("/", a.handleDeleteAccount) r.Post("/import", a.handleImportZones) }) }) r.Route("/templates", func(r chi.Router) { r.Post("/", a.handleCreateTemplate) r.Get("/", a.handleListTemplates) r.Route("/{tid}", func(r chi.Router) { r.Put("/", a.handleUpdateTemplate) r.Delete("/", a.handleDeleteTemplate) }) }) }) return r }