38 lines
1.2 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|