Add role ratelimit (#1)

* Add ratelimit configuration.
* Add rate limiting :party:
This commit is contained in:
2023-10-09 17:37:54 +01:00
parent 743eed7f71
commit ac44056a00
12 changed files with 185 additions and 17 deletions
+84
View File
@@ -0,0 +1,84 @@
package main
import (
"os"
"time"
goratecounter "github.com/lukaszraczylo/go-ratecounter"
)
type RateLimitConfig struct {
Req int `json:"req"`
Interval string `json:"interval"`
RateCounterTicker *goratecounter.RateCounter
}
var rateLimits map[string]RateLimitConfig
var ratelimit_intervals = map[string]time.Duration{
"milli": time.Millisecond,
"micro": time.Microsecond,
"nano": time.Nanosecond,
"second": time.Second,
"minute": time.Minute,
"hour": time.Hour,
"day": time.Hour * 24,
}
func loadRatelimitConfig() error {
paths := [3]string{"/app/ratelimit.json", "./ratelimit.json", "./static/default-ratelimit.json"}
for _, path := range paths {
file, err := os.Open(path)
if err != nil {
continue
}
defer file.Close()
decoder := json.NewDecoder(file)
config := struct {
RateLimit map[string]RateLimitConfig `json:"ratelimit"`
}{}
err = decoder.Decode(&config)
if err != nil {
return err
}
for key, value := range config.RateLimit {
value.RateCounterTicker = goratecounter.NewRateCounter().WithConfig(goratecounter.RateCounterConfig{
Interval: time.Duration(value.Req) * ratelimit_intervals[value.Interval],
})
cfg.Logger.Debug("Setting ratelimit config for role", map[string]interface{}{"role": key, "interval_provided": value.Interval, "interval_used": ratelimit_intervals[value.Interval], "ratelimit": value.Req})
config.RateLimit[key] = value
}
rateLimits = config.RateLimit
cfg.Logger.Debug("Rate limit config loaded", map[string]interface{}{"ratelimit": rateLimits})
return nil
}
cfg.Logger.Debug("Rate limit config not found")
return os.ErrNotExist
}
func rateLimitedRequest(userId string, userRole string) (shouldAllow bool) {
if rateLimits == nil {
cfg.Logger.Debug("Rate limit config not found", map[string]interface{}{"user_role": userRole})
return true
}
// check if userRole is in rateLimits
if _, ok := rateLimits[userRole]; !ok {
cfg.Logger.Warning("Rate limit role not found", map[string]interface{}{"user_role": userRole})
return true
}
if rateLimits[userRole].RateCounterTicker == nil {
cfg.Logger.Warning("Rate limit ticker not found", map[string]interface{}{"user_role": userRole})
return true
}
rateLimits[userRole].RateCounterTicker.Incr(1)
ticker_rate := rateLimits[userRole].RateCounterTicker.GetRate()
cfg.Logger.Debug("Rate limit ticker", map[string]interface{}{"user_role": userRole, "user_id": userId, "rate": ticker_rate, "config_rate": rateLimits[userRole].Req, "interval": rateLimits[userRole].Interval, "interval_duration": rateLimits[userRole].Interval})
if ticker_rate > float64(rateLimits[userRole].Req) {
cfg.Logger.Debug("Rate limit exceeded", map[string]interface{}{"user_role": userRole, "user_id": userId, "rate": ticker_rate, "config_rate": rateLimits[userRole].Req, "interval": rateLimits[userRole].Interval, "interval_duration": rateLimits[userRole].Interval})
return false
}
return true
}