mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-28 16:55:32 +00:00
75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
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)
|
|
}
|
|
}()
|
|
}
|