From 7df2fad568bb045da98a3d4961f8cb96baaa1a80 Mon Sep 17 00:00:00 2001 From: jedarden Date: Wed, 22 Apr 2026 17:37:34 -0400 Subject: [PATCH] =?UTF-8?q?feat(api):=20wire=20voteLtr=20rate=20limiter=20?= =?UTF-8?q?for=20upvote=20endpoint=20(=C2=A713.6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dedicated 10/hour-per-IP rate limiter for POST /api/feedback/{id}/upvote, separate from the 20/hour feedback submission limiter. Wired in main.go init, server_test.go helper, and RegisterRoutes. Co-Authored-By: Claude Sonnet 4.6 --- cmd/acb-api/main.go | 2 ++ cmd/acb-api/server.go | 6 +++++- cmd/acb-api/server_test.go | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/acb-api/main.go b/cmd/acb-api/main.go index 02143cc..020c6be 100644 --- a/cmd/acb-api/main.go +++ b/cmd/acb-api/main.go @@ -84,6 +84,7 @@ func main() { feedbackLtr: ratelimit.NewLimiter(20, 20.0/3600), // 20/hour per IP predictLtr: ratelimit.NewLimiter(60, 60.0/3600), // 60/hour per IP submitLtr: ratelimit.NewLimiter(5, 5.0/86400), // 5/day per key + voteLtr: ratelimit.NewLimiter(10, 10.0/3600), // 10/hour per IP } // Periodically purge stale rate-limit buckets (every 10 min) @@ -95,6 +96,7 @@ func main() { srv.feedbackLtr.Cleanup(time.Hour) srv.predictLtr.Cleanup(time.Hour) srv.submitLtr.Cleanup(24 * time.Hour) + srv.voteLtr.Cleanup(time.Hour) } }() diff --git a/cmd/acb-api/server.go b/cmd/acb-api/server.go index 6368712..d58d19c 100644 --- a/cmd/acb-api/server.go +++ b/cmd/acb-api/server.go @@ -30,6 +30,7 @@ type Server struct { feedbackLtr *ratelimit.Limiter // 20/hour per IP predictLtr *ratelimit.Limiter // 60/hour per IP submitLtr *ratelimit.Limiter // 5/day per bot_id + voteLtr *ratelimit.Limiter // 10/hour per IP } func (s *Server) RegisterRoutes(mux *http.ServeMux) { @@ -69,9 +70,12 @@ func (s *Server) RegisterRoutes(mux *http.ServeMux) { fbMW := s.feedbackLtr.Middleware(ipKey, func() { metrics.RateLimitHits.WithLabelValues("feedback").Inc() }) + voteMW := s.voteLtr.Middleware(ipKey, func() { + metrics.RateLimitHits.WithLabelValues("vote").Inc() + }) mux.HandleFunc("POST /api/feedback", fbMW(http.HandlerFunc(s.handleUIFeedback)).ServeHTTP) mux.HandleFunc("GET /api/feedback/", s.handleGetFeedback) - mux.HandleFunc("POST /api/feedback/", fbMW(http.HandlerFunc(s.handleFeedbackUpvote)).ServeHTTP) + mux.HandleFunc("POST /api/feedback/", voteMW(http.HandlerFunc(s.handleFeedbackUpvote)).ServeHTTP) // Predictions — 60/hour per IP predMW := s.predictLtr.Middleware(ipKey, func() { diff --git a/cmd/acb-api/server_test.go b/cmd/acb-api/server_test.go index c54de61..8edc9d4 100644 --- a/cmd/acb-api/server_test.go +++ b/cmd/acb-api/server_test.go @@ -22,6 +22,7 @@ func newTestServer() *Server { feedbackLtr: ratelimit.NewLimiter(20, 20.0/3600), predictLtr: ratelimit.NewLimiter(60, 60.0/3600), submitLtr: ratelimit.NewLimiter(5, 5.0/86400), + voteLtr: ratelimit.NewLimiter(10, 10.0/3600), } }