package server import ( "net/http" "github.com/gin-gonic/gin" "scrabble/backend/internal/ratewatch" ) // rateLimitReportRequest mirrors the gateway's periodic rejection summary: every // entry aggregates one limiter key (class + key) over the report window. type rateLimitReportRequest struct { WindowSeconds int `json:"window_seconds"` Entries []rateLimitReportEntry `json:"entries"` } // rateLimitReportEntry is one (class, key) aggregate of the report. type rateLimitReportEntry struct { Class string `json:"class"` Key string `json:"key"` Rejected int `json:"rejected"` } // handleRateLimitReport ingests one gateway rejection report into the rate // watch — the admin console's throttled view and the high-rate auto-flag. // Internal, gateway-only: like sessions/resolve it trusts the network segment. // Malformed individual entries are skipped by the watch itself. func (s *Server) handleRateLimitReport(c *gin.Context) { var req rateLimitReportRequest if err := c.ShouldBindJSON(&req); err != nil { abortBadRequest(c, "invalid rate-limit report") return } entries := make([]ratewatch.Entry, 0, len(req.Entries)) for _, e := range req.Entries { entries = append(entries, ratewatch.Entry{Class: e.Class, Key: e.Key, Rejected: e.Rejected}) } s.ratewatch.Ingest(c.Request.Context(), entries) c.Status(http.StatusNoContent) }