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 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 } // 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.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 }