email-verification-rate-limit.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package middleware
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/QuantumNous/new-api/common"
  8. "github.com/gin-gonic/gin"
  9. )
  10. const (
  11. EmailVerificationRateLimitMark = "EV"
  12. EmailVerificationMaxRequests = 2 // 30秒内最多2次
  13. EmailVerificationDuration = 30 // 30秒时间窗口
  14. )
  15. func redisEmailVerificationRateLimiter(c *gin.Context) {
  16. ctx := context.Background()
  17. rdb := common.RDB
  18. key := "emailVerification:" + EmailVerificationRateLimitMark + ":" + c.ClientIP()
  19. count, err := rdb.Incr(ctx, key).Result()
  20. if err != nil {
  21. // fallback
  22. memoryEmailVerificationRateLimiter(c)
  23. return
  24. }
  25. // 第一次设置键时设置过期时间
  26. if count == 1 {
  27. _ = rdb.Expire(ctx, key, time.Duration(EmailVerificationDuration)*time.Second).Err()
  28. }
  29. // 检查是否超出限制
  30. if count <= int64(EmailVerificationMaxRequests) {
  31. c.Next()
  32. return
  33. }
  34. // 获取剩余等待时间
  35. ttl, err := rdb.TTL(ctx, key).Result()
  36. waitSeconds := int64(EmailVerificationDuration)
  37. if err == nil && ttl > 0 {
  38. waitSeconds = int64(ttl.Seconds())
  39. }
  40. c.JSON(http.StatusTooManyRequests, gin.H{
  41. "success": false,
  42. "message": fmt.Sprintf("发送过于频繁,请等待 %d 秒后再试", waitSeconds),
  43. })
  44. c.Abort()
  45. }
  46. func memoryEmailVerificationRateLimiter(c *gin.Context) {
  47. key := EmailVerificationRateLimitMark + ":" + c.ClientIP()
  48. if !inMemoryRateLimiter.Request(key, EmailVerificationMaxRequests, EmailVerificationDuration) {
  49. c.JSON(http.StatusTooManyRequests, gin.H{
  50. "success": false,
  51. "message": "发送过于频繁,请稍后再试",
  52. })
  53. c.Abort()
  54. return
  55. }
  56. c.Next()
  57. }
  58. func EmailVerificationRateLimit() gin.HandlerFunc {
  59. return func(c *gin.Context) {
  60. if common.RedisEnabled {
  61. redisEmailVerificationRateLimiter(c)
  62. } else {
  63. inMemoryRateLimiter.Init(common.RateLimitKeyExpirationDuration)
  64. memoryEmailVerificationRateLimiter(c)
  65. }
  66. }
  67. }