97 lines
1.8 KiB
Go
97 lines
1.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"aggios-app/backend/internal/config"
|
|
)
|
|
|
|
type rateLimiter struct {
|
|
mu sync.Mutex
|
|
attempts map[string][]time.Time
|
|
maxAttempts int
|
|
}
|
|
|
|
func newRateLimiter(maxAttempts int) *rateLimiter {
|
|
rl := &rateLimiter{
|
|
attempts: make(map[string][]time.Time),
|
|
maxAttempts: maxAttempts,
|
|
}
|
|
|
|
// Clean old entries every minute
|
|
go func() {
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
rl.cleanup()
|
|
}
|
|
}()
|
|
|
|
return rl
|
|
}
|
|
|
|
func (rl *rateLimiter) cleanup() {
|
|
rl.mu.Lock()
|
|
defer rl.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
for ip, attempts := range rl.attempts {
|
|
var valid []time.Time
|
|
for _, t := range attempts {
|
|
if now.Sub(t) < time.Minute {
|
|
valid = append(valid, t)
|
|
}
|
|
}
|
|
if len(valid) == 0 {
|
|
delete(rl.attempts, ip)
|
|
} else {
|
|
rl.attempts[ip] = valid
|
|
}
|
|
}
|
|
}
|
|
|
|
func (rl *rateLimiter) isAllowed(ip string) bool {
|
|
rl.mu.Lock()
|
|
defer rl.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
attempts := rl.attempts[ip]
|
|
|
|
// Filter attempts within the last minute
|
|
var validAttempts []time.Time
|
|
for _, t := range attempts {
|
|
if now.Sub(t) < time.Minute {
|
|
validAttempts = append(validAttempts, t)
|
|
}
|
|
}
|
|
|
|
if len(validAttempts) >= rl.maxAttempts {
|
|
return false
|
|
}
|
|
|
|
validAttempts = append(validAttempts, now)
|
|
rl.attempts[ip] = validAttempts
|
|
|
|
return true
|
|
}
|
|
|
|
// RateLimit limits requests per IP address
|
|
func RateLimit(cfg *config.Config) func(http.Handler) http.Handler {
|
|
limiter := newRateLimiter(cfg.Security.MaxAttemptsPerMin)
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
ip := r.RemoteAddr
|
|
|
|
if !limiter.isAllowed(ip) {
|
|
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|