package ratelimit import ( "net/http" "sync" "time" "aijianzhan/platform/internal/authx" ) type Limiter struct { mu sync.Mutex visitors map[string]*visitor rate int window time.Duration } type visitor struct { count int reset time.Time } func New(ratePerMinute int) *Limiter { if ratePerMinute <= 0 { ratePerMinute = 120 } return &Limiter{ visitors: map[string]*visitor{}, rate: ratePerMinute, window: time.Minute, } } func (l *Limiter) Middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { key := r.RemoteAddr if tid := authx.TenantID(r.Context()); tid > 0 { key = "t:" + itoa(tid) } if !l.allow(key) { authx.WriteError(w, http.StatusTooManyRequests, "rate limit exceeded") return } next(w, r) } } func (l *Limiter) allow(key string) bool { l.mu.Lock() defer l.mu.Unlock() now := time.Now() v, ok := l.visitors[key] if !ok || now.After(v.reset) { l.visitors[key] = &visitor{count: 1, reset: now.Add(l.window)} return true } if v.count >= l.rate { return false } v.count++ return true } func itoa(n int64) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } var b [20]byte i := len(b) for n > 0 { i-- b[i] = byte('0' + n%10) n /= 10 } if neg { i-- b[i] = '-' } return string(b[i:]) }