feat: sync durable moderation and appeals
This commit is contained in:
parent
e1a95c7318
commit
9f467f4be7
140 changed files with 13730 additions and 316 deletions
|
|
@ -67,6 +67,12 @@ func (s *server) routes() http.Handler {
|
|||
mux.Handle("GET /api/gifts/{id}/animation", s.requireAuthAPI(http.HandlerFunc(s.handleStarGiftAnimationAPI)))
|
||||
mux.Handle("GET /api/gifts/{id}/collectibles", s.requireAuthAPI(http.HandlerFunc(s.handleStarGiftCollectiblesAPI)))
|
||||
mux.Handle("GET /api/gifts/{id}/collectibles/{kind}/{attribute_id}/animation", s.requireAuthAPI(http.HandlerFunc(s.handleStarGiftCollectibleAnimationAPI)))
|
||||
mux.Handle("GET /api/moderation/cases", s.requireAuthAPI(http.HandlerFunc(s.handleModerationCasesAPI)))
|
||||
mux.Handle("GET /api/moderation/cases/{id}", s.requireAuthAPI(http.HandlerFunc(s.handleModerationCaseAPI)))
|
||||
mux.Handle("GET /api/moderation/reports/{id}", s.requireAuthAPI(http.HandlerFunc(s.handleModerationReportAPI)))
|
||||
mux.Handle("POST /api/moderation/cases/{id}/claim", s.requireAuthAPI(http.HandlerFunc(s.handleClaimModerationCaseAPI)))
|
||||
mux.Handle("POST /api/moderation/cases/{id}/decide", s.requireAuthAPI(http.HandlerFunc(s.handleDecideModerationCaseAPI)))
|
||||
mux.Handle("POST /api/moderation/cases/{id}/appeals/{appeal_id}/review", s.requireAuthAPI(http.HandlerFunc(s.handleReviewModerationAppealAPI)))
|
||||
mux.Handle("POST /api/actions/set-frozen", s.requireAuthAPI(http.HandlerFunc(s.handleSetAccountFrozenAPI)))
|
||||
mux.Handle("POST /api/actions/grant-premium", s.requireAuthAPI(http.HandlerFunc(s.handleGrantPremiumAPI)))
|
||||
mux.Handle("POST /api/actions/grant-stars", s.requireAuthAPI(http.HandlerFunc(s.handleGrantStarsAPI)))
|
||||
|
|
@ -367,6 +373,110 @@ func (s *server) proxyAdminJSON(w http.ResponseWriter, r *http.Request, apiPath
|
|||
_, _ = w.Write(raw)
|
||||
}
|
||||
|
||||
func (s *server) handleModerationCasesAPI(w http.ResponseWriter, r *http.Request) {
|
||||
apiPath := "/v1/moderation/cases"
|
||||
if r.URL.RawQuery != "" {
|
||||
apiPath += "?" + r.URL.RawQuery
|
||||
}
|
||||
s.proxyAdminJSON(w, r, apiPath, 4<<20)
|
||||
}
|
||||
|
||||
func (s *server) handleModerationCaseAPI(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
writeAPIError(w, http.StatusBadRequest, "invalid moderation case id")
|
||||
return
|
||||
}
|
||||
s.proxyAdminJSON(w, r, fmt.Sprintf("/v1/moderation/cases/%d", id), 4<<20)
|
||||
}
|
||||
|
||||
func (s *server) handleModerationReportAPI(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
writeAPIError(w, http.StatusBadRequest, "invalid moderation report id")
|
||||
return
|
||||
}
|
||||
s.proxyAdminJSON(w, r, fmt.Sprintf("/v1/moderation/reports/%d", id), 4<<20)
|
||||
}
|
||||
|
||||
func (s *server) handleClaimModerationCaseAPI(w http.ResponseWriter, r *http.Request) {
|
||||
s.proxyModerationWrite(w, r, "claim", false)
|
||||
}
|
||||
|
||||
func (s *server) handleDecideModerationCaseAPI(w http.ResponseWriter, r *http.Request) {
|
||||
s.proxyModerationWrite(w, r, "decide", true)
|
||||
}
|
||||
|
||||
func (s *server) handleReviewModerationAppealAPI(w http.ResponseWriter, r *http.Request) {
|
||||
s.proxyModerationWrite(w, r, "appeals/"+r.PathValue("appeal_id")+"/review", true)
|
||||
}
|
||||
|
||||
func (s *server) proxyModerationWrite(w http.ResponseWriter, r *http.Request, suffix string, needsCommand bool) {
|
||||
caseID, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
||||
if err != nil || caseID <= 0 {
|
||||
writeAPIError(w, http.StatusBadRequest, "invalid moderation case id")
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(suffix, "appeals/") {
|
||||
appealID, err := strconv.ParseInt(r.PathValue("appeal_id"), 10, 64)
|
||||
if err != nil || appealID <= 0 {
|
||||
writeAPIError(w, http.StatusBadRequest, "invalid moderation appeal id")
|
||||
return
|
||||
}
|
||||
}
|
||||
defer r.Body.Close()
|
||||
var payload map[string]any
|
||||
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20))
|
||||
if err := decoder.Decode(&payload); err != nil {
|
||||
writeAPIError(w, http.StatusBadRequest, "invalid json")
|
||||
return
|
||||
}
|
||||
payload["actor"] = actorFromContext(r.Context())
|
||||
if needsCommand {
|
||||
commandID, _ := payload["command_id"].(string)
|
||||
if strings.TrimSpace(commandID) == "" {
|
||||
payload["command_id"] = newCommandID("moderation")
|
||||
}
|
||||
}
|
||||
raw, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
writeAPIError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
s.relayAdminJSON(
|
||||
w, r, http.MethodPost,
|
||||
fmt.Sprintf("/v1/moderation/cases/%d/%s", caseID, suffix),
|
||||
raw, 4<<20,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *server) relayAdminJSON(w http.ResponseWriter, r *http.Request, method, apiPath string, body []byte, maxBytes int64) {
|
||||
req, err := http.NewRequestWithContext(
|
||||
r.Context(), method, s.cfg.AdminAPIURL+apiPath, bytes.NewReader(body),
|
||||
)
|
||||
if err != nil {
|
||||
writeAPIError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+s.cfg.AdminAPIToken)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
writeAPIError(w, http.StatusBadGateway, err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
raw, err := io.ReadAll(io.LimitReader(resp.Body, maxBytes+1))
|
||||
if err != nil || int64(len(raw)) > maxBytes {
|
||||
writeAPIError(w, http.StatusBadGateway, "invalid admin api response")
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
_, _ = w.Write(raw)
|
||||
}
|
||||
|
||||
func (s *server) handleAccountsAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if s.read == nil {
|
||||
writeAPIError(w, http.StatusServiceUnavailable, "read store is not configured")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue