mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2025-10-29 01:05:32 +00:00
rate limit
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/app"
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/helpers/security"
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/middleware"
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels"
|
||||
"github.com/Theodor-Springmann-Stiftung/musenalm/templating"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
@@ -48,7 +49,8 @@ type LoginPage struct {
|
||||
|
||||
func (p *LoginPage) Setup(router *router.Router[*core.RequestEvent], app core.App, engine *templating.Engine) error {
|
||||
router.GET(URL_LOGIN, p.GET(engine, app))
|
||||
router.POST(URL_LOGIN, p.POST(engine, app))
|
||||
r := router.POST(URL_LOGIN, p.POST(engine, app))
|
||||
r.BindFunc(middleware.RateLimiter(30, time.Minute*2, time.Hour*6))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user