Files
XQKqueue/server/internal/httpapi/limiter.go
2026-07-12 15:53:24 +08:00

61 lines
1.2 KiB
Go

package httpapi
import (
"sync"
"time"
)
type loginAttempt struct {
failures int
windowStart time.Time
blockedTill time.Time
}
type loginLimiter struct {
mu sync.Mutex
attempts map[string]loginAttempt
now func() time.Time
}
func newLoginLimiter(now func() time.Time) *loginLimiter {
return &loginLimiter{attempts: make(map[string]loginAttempt), now: now}
}
func (l *loginLimiter) allow(key string) (bool, time.Duration) {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
attempt, ok := l.attempts[key]
if !ok {
return true, 0
}
if now.Before(attempt.blockedTill) {
return false, attempt.blockedTill.Sub(now)
}
if now.Sub(attempt.windowStart) > 10*time.Minute {
delete(l.attempts, key)
}
return true, 0
}
func (l *loginLimiter) failure(key string) {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
attempt := l.attempts[key]
if attempt.windowStart.IsZero() || now.Sub(attempt.windowStart) > 10*time.Minute {
attempt = loginAttempt{windowStart: now}
}
attempt.failures++
if attempt.failures >= 5 {
attempt.blockedTill = now.Add(10 * time.Minute)
}
l.attempts[key] = attempt
}
func (l *loginLimiter) success(key string) {
l.mu.Lock()
delete(l.attempts, key)
l.mu.Unlock()
}