mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-29 17:25:32 +00:00
rate limit
This commit is contained in:
74
middleware/rate-limiter.go
Normal file
74
middleware/rate-limiter.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
type clientStats struct {
|
||||
count int
|
||||
windowStart time.Time
|
||||
}
|
||||
|
||||
func RateLimiter(limit int, windowDuration time.Duration, cleanupInterval time.Duration) func(*core.RequestEvent) error {
|
||||
clientRequests := make(map[string]*clientStats)
|
||||
var mu sync.Mutex
|
||||
|
||||
startPeriodicCleanup(clientRequests, &mu, windowDuration, cleanupInterval)
|
||||
|
||||
return func(e *core.RequestEvent) error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipStr := e.RealIP()
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
stats, exists := clientRequests[ipStr]
|
||||
now := time.Now()
|
||||
|
||||
if !exists || now.After(stats.windowStart.Add(windowDuration)) {
|
||||
clientRequests[ipStr] = &clientStats{
|
||||
count: 1,
|
||||
windowStart: now,
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
if stats.count >= limit {
|
||||
return e.Error(http.StatusTooManyRequests, "Too Many Requests", nil)
|
||||
}
|
||||
|
||||
stats.count++
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func performCleanupCycle(clientRequests map[string]*clientStats, mu *sync.Mutex, windowDuration time.Duration) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
maxEntryAge := 2 * windowDuration
|
||||
for ip, stats := range clientRequests {
|
||||
if now.After(stats.windowStart.Add(maxEntryAge)) {
|
||||
delete(clientRequests, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startPeriodicCleanup(clientRequests map[string]*clientStats, mu *sync.Mutex, windowDuration time.Duration, cleanupInterval time.Duration) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(cleanupInterval)
|
||||
defer ticker.Stop() // Stop the ticker when this goroutine exits (though it runs indefinitely here)
|
||||
|
||||
for range ticker.C {
|
||||
performCleanupCycle(clientRequests, mu, windowDuration)
|
||||
}
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user