veylant/internal/middleware/ratelimit.go
2026-02-23 13:35:04 +01:00

38 lines
1.2 KiB
Go

package middleware
import (
"net/http"
"github.com/veylant/ia-gateway/internal/apierror"
"github.com/veylant/ia-gateway/internal/ratelimit"
)
// RateLimit returns a middleware that enforces per-tenant and per-user request
// rate limits using token buckets. On limit exceeded it writes HTTP 429 in the
// OpenAI-compatible JSON error format and aborts the request.
//
// The middleware must run AFTER the Auth middleware so that tenant/user claims
// are available in the request context. If no claims are present (anonymous
// request that wasn't rejected by Auth), the request passes through.
func RateLimit(limiter *ratelimit.Limiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := ClaimsFromContext(r.Context())
if !ok || claims == nil {
// Auth middleware handles unauthenticated requests separately.
next.ServeHTTP(w, r)
return
}
if !limiter.Allow(claims.TenantID, claims.UserID) {
apierror.WriteErrorWithRequestID(w, apierror.NewRateLimitError(
"rate limit exceeded — try again later",
), RequestIDFromContext(r.Context()))
return
}
next.ServeHTTP(w, r)
})
}
}