package v1 import ( "encoding/json" "fmt" "net" "net/http" "strings" "github.com/gorilla/mux" "github.com/wild-cloud/wild-central/internal/services" ) // ServicesListAll lists all registered services. func (api *API) ServicesListAll(w http.ResponseWriter, r *http.Request) { svcs, err := api.services.List() if err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to list services: %v", err)) return } if svcs == nil { svcs = []services.Service{} } respondJSON(w, http.StatusOK, map[string]any{ "services": svcs, }) } // ServicesGet retrieves a service by domain. func (api *API) ServicesGet(w http.ResponseWriter, r *http.Request) { domain := mux.Vars(r)["domain"] svc, err := api.services.Get(domain) if err != nil { respondError(w, http.StatusNotFound, fmt.Sprintf("Service not found: %v", err)) return } respondJSON(w, http.StatusOK, svc) } // ServicesRegister creates or updates a service registration. func (api *API) ServicesRegister(w http.ResponseWriter, r *http.Request) { var svc services.Service if err := json.NewDecoder(r.Body).Decode(&svc); err != nil { respondError(w, http.StatusBadRequest, fmt.Sprintf("Invalid request body: %v", err)) return } // Validate routes for i, r := range svc.Routes { for _, p := range r.Paths { if !strings.HasPrefix(p, "/") { respondError(w, http.StatusBadRequest, fmt.Sprintf("Route %d: path prefix must start with /: %q", i, p)) return } } for _, cidr := range r.IPAllow { if _, _, err := net.ParseCIDR(cidr); err != nil { respondError(w, http.StatusBadRequest, fmt.Sprintf("Route %d: invalid CIDR in ipAllow: %q", i, cidr)) return } } } if err := api.services.Register(svc); err != nil { respondError(w, http.StatusBadRequest, fmt.Sprintf("Failed to register service: %v", err)) return } // Return the stored version (has defaults applied) stored, _ := api.services.Get(svc.Domain) respondJSON(w, http.StatusCreated, map[string]any{ "message": fmt.Sprintf("Service %q registered", svc.Domain), "service": stored, }) } // ServicesUpdate applies partial updates to a service. func (api *API) ServicesUpdate(w http.ResponseWriter, r *http.Request) { domain := mux.Vars(r)["domain"] var updates map[string]any if err := json.NewDecoder(r.Body).Decode(&updates); err != nil { respondError(w, http.StatusBadRequest, fmt.Sprintf("Invalid request body: %v", err)) return } if err := api.services.Update(domain, updates); err != nil { respondError(w, http.StatusNotFound, fmt.Sprintf("Failed to update service: %v", err)) return } svc, _ := api.services.Get(domain) respondJSON(w, http.StatusOK, map[string]any{ "message": fmt.Sprintf("Service %q updated", domain), "service": svc, }) } // ServicesDeregister removes a service registration. func (api *API) ServicesDeregister(w http.ResponseWriter, r *http.Request) { domain := mux.Vars(r)["domain"] if err := api.services.Deregister(domain); err != nil { respondError(w, http.StatusNotFound, fmt.Sprintf("Failed to deregister service: %v", err)) return } respondMessage(w, http.StatusOK, fmt.Sprintf("Service %q deregistered", domain)) } // ServicesDeregisterBySource removes all services from a source with a given backend. func (api *API) ServicesDeregisterBySource(w http.ResponseWriter, r *http.Request) { source := r.URL.Query().Get("source") backend := r.URL.Query().Get("backend") if source == "" { respondError(w, http.StatusBadRequest, "source query parameter is required") return } if err := api.services.DeregisterBySource(source, backend); err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to deregister: %v", err)) return } respondMessage(w, http.StatusOK, fmt.Sprintf("Services from %q deregistered", source)) }