package main import ( "errors" "net/http" "strings" "github.com/go-chi/chi/v5" ) func (a *app) platformAdminMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { claims, _ := r.Context().Value(userClaimsKey).(*accessClaims) if claims == nil { writeError(w, http.StatusUnauthorized, "no authenticated user") return } if !hasAnyRole(claims, "admin", "platform_owner", "platform_admin") { writeError(w, http.StatusForbidden, "platform admin role required") return } next.ServeHTTP(w, r) }) } func (a *app) listTenants(w http.ResponseWriter, r *http.Request) { docs, err := a.list("tenants", Doc{}, Sort{{Key: "name", Value: 1}}) if err != nil { writeError(w, http.StatusInternalServerError, "failed to list tenants") return } writeJSON(w, http.StatusOK, docs) } func (a *app) createTenant(w http.ResponseWriter, r *http.Request) { var payload map[string]any if err := decodeJSON(r, &payload); err != nil { writeError(w, http.StatusBadRequest, "invalid payload") return } name := strings.TrimSpace(anyToString(payload["name"])) if name == "" { writeError(w, http.StatusBadRequest, "name is required") return } payload["name"] = name if slug := strings.TrimSpace(anyToString(payload["slug"])); slug != "" { payload["slug"] = slugify(slug) } else { payload["slug"] = slugify(name) } doc, err := a.create("tenants", Doc(payload)) if err != nil { writeError(w, http.StatusInternalServerError, "failed to create tenant") return } writeJSON(w, http.StatusCreated, doc) } func (a *app) getTenant(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") doc, err := a.getByID("tenants", id) if err != nil { if errors.Is(err, ErrNotFound) { writeError(w, http.StatusNotFound, "tenant not found") return } writeError(w, http.StatusInternalServerError, "failed to load tenant") return } writeJSON(w, http.StatusOK, doc) } func (a *app) patchTenant(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var patch map[string]any if err := decodeJSON(r, &patch); err != nil { writeError(w, http.StatusBadRequest, "invalid patch payload") return } if rawName, ok := patch["name"]; ok { name := strings.TrimSpace(anyToString(rawName)) if name == "" { writeError(w, http.StatusBadRequest, "name cannot be empty") return } patch["name"] = name } if rawSlug, ok := patch["slug"]; ok { slug := strings.TrimSpace(anyToString(rawSlug)) if slug == "" { delete(patch, "slug") } else { patch["slug"] = slugify(slug) } } doc, err := a.patch("tenants", id, Doc(patch)) if err != nil { if errors.Is(err, ErrNotFound) { writeError(w, http.StatusNotFound, "tenant not found") return } writeError(w, http.StatusInternalServerError, "failed to update tenant") return } writeJSON(w, http.StatusOK, doc) } func (a *app) deleteTenant(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if err := a.deleteByID("tenants", id); err != nil { if errors.Is(err, ErrNotFound) { writeError(w, http.StatusNotFound, "tenant not found") return } writeError(w, http.StatusInternalServerError, "failed to delete tenant") return } w.WriteHeader(http.StatusNoContent) }