65dc805eb5
- 前端: ESLint+Prettier配置、Tailwind v4配置、ErrorBoundary、全局AuthLoader优化、ReactQuery分层 - 后端: MinIO凭证移除、Docker统一为govai品牌、zerolog日志封装、错误码枚举、文件上传校验、单元测试(13项全通过) - 运维: 健康检查增强(PG/Redis ping)、Prometheus指标(/metrics端点)、多租户tenant包、RateLimit nil防御 - 移动: citation_prompt.txt → internal/assets/
45 lines
979 B
Go
45 lines
979 B
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/enterprise-ai-platform/server/internal/response"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// RateLimit creates a per-user rate limiter using Redis sliding window.
|
|
func RateLimit(rdb *redis.Client, maxRequests int, window time.Duration) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if rdb == nil {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
userID := GetUserID(r.Context())
|
|
key := fmt.Sprintf("rl:%s:%s", userID.String(), r.URL.Path)
|
|
|
|
ctx := context.Background()
|
|
count, err := rdb.Incr(ctx, key).Result()
|
|
if err != nil {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
if count == 1 {
|
|
rdb.Expire(ctx, key, window)
|
|
}
|
|
|
|
if count > int64(maxRequests) {
|
|
response.TooManyRequests(w, "请求过于频繁,请稍后再试")
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|