package auth import ( "net/http" "sync" "time" "github.com/gin-gonic/gin" ) type visitor struct { count int lastSeen time.Time } type RateLimiter struct { mu sync.Mutex visitors map[string]*visitor rate int window time.Duration } func NewRateLimiter(rate int, window time.Duration) *RateLimiter { rl := &RateLimiter{ visitors: make(map[string]*visitor), rate: rate, window: window, } go rl.cleanup() return rl } func (rl *RateLimiter) cleanup() { ticker := time.NewTicker(10 * time.Minute) defer ticker.Stop() for range ticker.C { rl.mu.Lock() now := time.Now() for ip, v := range rl.visitors { if now.Sub(v.lastSeen) > rl.window*2 { delete(rl.visitors, ip) } } rl.mu.Unlock() } } func (rl *RateLimiter) Middleware() gin.HandlerFunc { return func(c *gin.Context) { ip := c.ClientIP() rl.mu.Lock() v, exists := rl.visitors[ip] now := time.Now() if !exists || now.Sub(v.lastSeen) > rl.window { rl.visitors[ip] = &visitor{count: 1, lastSeen: now} rl.mu.Unlock() c.Next() return } v.count++ v.lastSeen = now if v.count > rl.rate { rl.mu.Unlock() c.JSON(http.StatusTooManyRequests, ErrorResponse{Error: "too many requests, try again later"}) c.Abort() return } rl.mu.Unlock() c.Next() } }