package middleware import ( "net/http" "sync" "time" "github.com/gin-gonic/gin" ) type visitor struct { count int lastSeen time.Time } type rateLimiter struct { visitors map[string]*visitor mu sync.Mutex limit int window time.Duration } func newRateLimiter(limit int, window time.Duration) *rateLimiter { rl := &rateLimiter{ visitors: make(map[string]*visitor), limit: limit, window: window, } go rl.cleanup() return rl } func (rl *rateLimiter) allow(ip string) bool { rl.mu.Lock() defer rl.mu.Unlock() v, exists := rl.visitors[ip] if !exists { rl.visitors[ip] = &visitor{count: 1, lastSeen: time.Now()} return true } if time.Since(v.lastSeen) > rl.window { v.count = 1 v.lastSeen = time.Now() return true } v.count++ v.lastSeen = time.Now() return v.count <= rl.limit } func (rl *rateLimiter) cleanup() { for { time.Sleep(10 * time.Minute) rl.mu.Lock() for ip, v := range rl.visitors { if time.Since(v.lastSeen) > rl.window*2 { delete(rl.visitors, ip) } } rl.mu.Unlock() } } var ( createLimiter = newRateLimiter(10, 1*time.Minute) generalLimiter = newRateLimiter(60, 1*time.Minute) ) func RateLimit(limit int) gin.HandlerFunc { if limit == 10 { return func(c *gin.Context) { if !createLimiter.allow(c.ClientIP()) { c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"}) return } c.Next() } } return func(c *gin.Context) { if !generalLimiter.allow(c.ClientIP()) { c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"}) return } c.Next() } }