package spindle import ( "crypto/subtle" "encoding/json" "fmt" "net/http" "github.com/bluesky-social/indigo/atproto/syntax" "github.com/go-chi/chi/v5" ) type adminReq struct { Did string `json:"did"` } func (s *Spindle) adminRouter() http.Handler { r := chi.NewRouter() r.Use(s.adminMiddleware) r.Post("/member/allow", s.handleAdminMemberAllow) r.Post("/member/block", s.handleAdminMemberBlock) return r } func (s *Spindle) adminMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { password := s.cfg.Server.AdminPassword u, p, ok := r.BasicAuth() valid := password != "" && ok && u == "admin" && subtle.ConstantTimeCompare([]byte(p), []byte(password)) == 1 if !valid { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } next.ServeHTTP(w, r) }) } func (s *Spindle) handleAdminMemberAllow(w http.ResponseWriter, r *http.Request) { var req adminReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "bad request body", http.StatusBadRequest) return } did, err := syntax.ParseDID(req.Did) if err != nil { http.Error(w, fmt.Sprintf("invalid did: %v", err), http.StatusBadRequest) return } if err := s.AllowMember(r.Context(), did); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } func (s *Spindle) handleAdminMemberBlock(w http.ResponseWriter, r *http.Request) { var req adminReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "bad request body", http.StatusBadRequest) return } did, err := syntax.ParseDID(req.Did) if err != nil { http.Error(w, fmt.Sprintf("invalid did: %v", err), http.StatusBadRequest) return } if err := s.BlockMember(r.Context(), did); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }