126 lines
5.5 KiB
Go
126 lines
5.5 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/owen/vibedns/internal/app"
|
|
)
|
|
|
|
// routes registers every REST endpoint.
|
|
//
|
|
// Paths follow the usual collection/item shape, and PUT and PATCH are both
|
|
// accepted for updates: the service layer carries forward any field a caller
|
|
// omits, so a partial update behaves the way PATCH callers expect.
|
|
func (s *Server) routes(mux *http.ServeMux) {
|
|
const v1 = "/api/v1"
|
|
|
|
// Zones.
|
|
mux.HandleFunc("GET "+v1+"/zones", s.h(s.listZones))
|
|
mux.HandleFunc("POST "+v1+"/zones", s.h(s.createZone))
|
|
mux.HandleFunc("GET "+v1+"/zones/{id}", s.h(s.getZone))
|
|
mux.HandleFunc("PUT "+v1+"/zones/{id}", s.h(s.updateZone))
|
|
mux.HandleFunc("PATCH "+v1+"/zones/{id}", s.h(s.updateZone))
|
|
mux.HandleFunc("DELETE "+v1+"/zones/{id}", s.h(s.deleteZone))
|
|
mux.HandleFunc("POST "+v1+"/zones/{id}/clone", s.h(s.cloneZone))
|
|
mux.HandleFunc("GET "+v1+"/zones/{id}/export", s.h(s.exportZone))
|
|
mux.HandleFunc("POST "+v1+"/zones/{id}/import", s.h(s.importZone))
|
|
|
|
// Records, both nested under a zone and flat.
|
|
mux.HandleFunc("GET "+v1+"/zones/{id}/records", s.h(s.listZoneRecords))
|
|
mux.HandleFunc("POST "+v1+"/zones/{id}/records", s.h(s.createRecord))
|
|
mux.HandleFunc("GET "+v1+"/records", s.h(s.listRecords))
|
|
mux.HandleFunc("GET "+v1+"/records/{id}", s.h(s.getRecord))
|
|
mux.HandleFunc("PUT "+v1+"/records/{id}", s.h(s.updateRecord))
|
|
mux.HandleFunc("PATCH "+v1+"/records/{id}", s.h(s.updateRecord))
|
|
mux.HandleFunc("DELETE "+v1+"/records/{id}", s.h(s.deleteRecord))
|
|
mux.HandleFunc("GET "+v1+"/record-types", s.h(s.listRecordTypes))
|
|
|
|
// Client networks.
|
|
mux.HandleFunc("GET "+v1+"/networks", s.h(s.listNetworks))
|
|
mux.HandleFunc("POST "+v1+"/networks", s.h(s.createNetwork))
|
|
mux.HandleFunc("GET "+v1+"/networks/{id}", s.h(s.getNetwork))
|
|
mux.HandleFunc("PUT "+v1+"/networks/{id}", s.h(s.updateNetwork))
|
|
mux.HandleFunc("PATCH "+v1+"/networks/{id}", s.h(s.updateNetwork))
|
|
mux.HandleFunc("DELETE "+v1+"/networks/{id}", s.h(s.deleteNetwork))
|
|
|
|
// Policies.
|
|
mux.HandleFunc("GET "+v1+"/policies", s.h(s.listPolicies))
|
|
mux.HandleFunc("POST "+v1+"/policies", s.h(s.createPolicy))
|
|
mux.HandleFunc("GET "+v1+"/policies/{id}", s.h(s.getPolicy))
|
|
mux.HandleFunc("PUT "+v1+"/policies/{id}", s.h(s.updatePolicy))
|
|
mux.HandleFunc("PATCH "+v1+"/policies/{id}", s.h(s.updatePolicy))
|
|
mux.HandleFunc("DELETE "+v1+"/policies/{id}", s.h(s.deletePolicy))
|
|
|
|
// Blacklists and allowlists share one implementation, differing only in
|
|
// the kind they filter and create.
|
|
for _, kind := range []string{"blacklists", "allowlists"} {
|
|
k := kind
|
|
mux.HandleFunc("GET "+v1+"/"+k, s.h(s.listListsFor(k)))
|
|
mux.HandleFunc("POST "+v1+"/"+k, s.h(s.createListFor(k)))
|
|
mux.HandleFunc("GET "+v1+"/"+k+"/{id}", s.h(s.getList))
|
|
mux.HandleFunc("PUT "+v1+"/"+k+"/{id}", s.h(s.updateList))
|
|
mux.HandleFunc("PATCH "+v1+"/"+k+"/{id}", s.h(s.updateList))
|
|
mux.HandleFunc("DELETE "+v1+"/"+k+"/{id}", s.h(s.deleteList))
|
|
mux.HandleFunc("GET "+v1+"/"+k+"/{id}/domains", s.h(s.listDomains))
|
|
mux.HandleFunc("POST "+v1+"/"+k+"/{id}/domains", s.h(s.addDomain))
|
|
mux.HandleFunc("DELETE "+v1+"/"+k+"/{id}/domains", s.h(s.clearDomains))
|
|
mux.HandleFunc("POST "+v1+"/"+k+"/{id}/import", s.h(s.importDomains))
|
|
mux.HandleFunc("GET "+v1+"/"+k+"/{id}/export", s.h(s.exportDomains))
|
|
}
|
|
mux.HandleFunc("DELETE "+v1+"/domains/{id}", s.h(s.deleteDomain))
|
|
|
|
// Cache.
|
|
mux.HandleFunc("GET "+v1+"/cache", s.h(s.getCache))
|
|
mux.HandleFunc("DELETE "+v1+"/cache", s.h(s.flushCache))
|
|
mux.HandleFunc("GET "+v1+"/cache/entries", s.h(s.listCacheEntries))
|
|
mux.HandleFunc("DELETE "+v1+"/cache/entries", s.h(s.deleteCacheEntry))
|
|
|
|
// Settings.
|
|
mux.HandleFunc("GET "+v1+"/settings", s.h(s.getSettings))
|
|
mux.HandleFunc("PUT "+v1+"/settings", s.h(s.updateSettings))
|
|
mux.HandleFunc("PATCH "+v1+"/settings", s.h(s.updateSettings))
|
|
|
|
// Statistics and logs.
|
|
mux.HandleFunc("GET "+v1+"/stats", s.h(s.getStats))
|
|
mux.HandleFunc("GET "+v1+"/querylog", s.h(s.listQueryLog))
|
|
mux.HandleFunc("DELETE "+v1+"/querylog", s.h(s.clearQueryLog))
|
|
mux.HandleFunc("GET "+v1+"/auditlog", s.h(s.listAuditLog))
|
|
|
|
// Operations.
|
|
mux.HandleFunc("GET "+v1+"/backups", s.h(s.listBackups))
|
|
mux.HandleFunc("POST "+v1+"/backups", s.h(s.createBackup))
|
|
mux.HandleFunc("GET "+v1+"/config/export", s.h(s.exportConfig))
|
|
mux.HandleFunc("POST "+v1+"/config/import", s.h(s.importConfig))
|
|
|
|
// Tools.
|
|
mux.HandleFunc("GET "+v1+"/tools/reverse-zone", s.h(s.reverseZone))
|
|
mux.HandleFunc("GET "+v1+"/tools/lookup", s.h(s.lookup))
|
|
mux.HandleFunc("GET "+v1+"/tools/domain-check", s.h(s.domainCheck))
|
|
|
|
// Service metadata. Both the bare path and the trailing-slash form serve
|
|
// the index, because ServeMux redirects the former to the latter.
|
|
mux.HandleFunc("GET "+v1, s.h(s.index))
|
|
mux.HandleFunc("GET "+v1+"/{$}", s.h(s.index))
|
|
mux.HandleFunc("GET "+v1+"/", s.h(s.notFound))
|
|
}
|
|
|
|
// index describes the API surface, so a caller can discover it with one GET.
|
|
func (s *Server) index(w http.ResponseWriter, r *http.Request) error {
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"version": "v1",
|
|
"resources": []string{
|
|
"/api/v1/zones", "/api/v1/records", "/api/v1/networks",
|
|
"/api/v1/policies", "/api/v1/blacklists", "/api/v1/allowlists",
|
|
"/api/v1/cache", "/api/v1/settings", "/api/v1/stats",
|
|
"/api/v1/querylog", "/api/v1/auditlog", "/api/v1/backups",
|
|
"/api/v1/config/export", "/api/v1/config/import",
|
|
"/api/v1/tools/lookup", "/api/v1/tools/reverse-zone",
|
|
},
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) notFound(w http.ResponseWriter, r *http.Request) error {
|
|
return app.NotFound("No API endpoint matches %s %s.", r.Method, r.URL.Path)
|
|
}
|