feat: Domain-keyed service registration model

Rewrite the service registration model per the architecture plan:

Model changes:
- Domain is the unique key (removed Name field)
- Removed ExtraDomains — each domain is its own registration
- Removed SourceNode, BackendStatic, ReachOff
- Added Subdomains bool for wildcard matching control
- Files stored as <domain_with_underscores>.yaml
- API routes: /services/{domain:.+} (regex for dots in path)
- Added DeregisterBySource for cleanup before re-registration

Reconciliation changes:
- No ExtraDomains iteration — each service IS one domain
- reach:internal → sets InternalDomain (generates local=/)
- reach:public → sets Domain (no local=/)
- tcp-passthrough → DNS points to backend IP
- http → DNS points to Central IP

All 22 tests pass (9 manager + 8 handler + 5 config/cert).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 20:23:11 +00:00
parent d009e095c0
commit 8874bc6281
7 changed files with 384 additions and 329 deletions

View File

@@ -113,7 +113,6 @@ func (api *API) RegisterSelf(port int) {
}
svc := services.Service{
Name: "wild-central",
Source: "wild-central",
Domain: domain,
Backend: services.Backend{
@@ -251,12 +250,13 @@ func (api *API) RegisterRoutes(r *mux.Router) {
r.HandleFunc("/api/v1/cloudflare/zone", api.CloudflareGetZoneID).Methods("GET")
r.HandleFunc("/api/v1/cloudflare/zone", api.CloudflareSetZoneID).Methods("POST")
// Service registration
// Service registration — domain is the unique key
r.HandleFunc("/api/v1/services", api.ServicesListAll).Methods("GET")
r.HandleFunc("/api/v1/services", api.ServicesRegister).Methods("POST")
r.HandleFunc("/api/v1/services/{name}", api.ServicesGet).Methods("GET")
r.HandleFunc("/api/v1/services/{name}", api.ServicesUpdate).Methods("PATCH")
r.HandleFunc("/api/v1/services/{name}", api.ServicesDeregister).Methods("DELETE")
r.HandleFunc("/api/v1/services/deregister", api.ServicesDeregisterBySource).Methods("DELETE")
r.HandleFunc("/api/v1/services/{domain:.+}", api.ServicesGet).Methods("GET")
r.HandleFunc("/api/v1/services/{domain:.+}", api.ServicesUpdate).Methods("PATCH")
r.HandleFunc("/api/v1/services/{domain:.+}", api.ServicesDeregister).Methods("DELETE")
// SSE events
r.HandleFunc("/api/v1/events", api.GlobalEventStream).Methods("GET")