summaryrefslogtreecommitdiffci
path: root/internal/router
diff options
from:
to:
context:
space:
mode:
authorGravatar SayaAndy <saya.andy@posteo.com> 2026-05-02 14:29:55 +0700
committerGravatar SayaAndy <saya.andy@posteo.com> 2026-05-02 14:29:55 +0700
commitca1f36376865cb2a20923aa62fdff3ee217abde3 (patch)
tree2e3676489327505dd2b4d53023ca0da6fc2dc5a2 /internal/router
parent3898baa47faf268864ecac3699f7e98c6cac01a4 (diff)
downloadweb-ca1f36376865cb2a20923aa62fdff3ee217abde3.tar.gz
web-ca1f36376865cb2a20923aa62fdff3ee217abde3.zip
feat: implement rate limiting
Diffstat (limited to 'internal/router')
-rw-r--r--internal/router/basic-handler.go4
-rw-r--r--internal/router/handlers/api-v1-blog-search.go4
-rw-r--r--internal/router/handlers/api-v1-email-send-verification-code.go4
-rw-r--r--internal/router/handlers/api-v1-email-verify.go4
-rw-r--r--internal/router/handlers/api-v1-like-put.go4
-rw-r--r--internal/router/handlers/api-v1-subs-put.go4
-rw-r--r--internal/router/rate-limiters.go37
-rw-r--r--internal/router/router.go5
8 files changed, 66 insertions, 0 deletions
diff --git a/internal/router/basic-handler.go b/internal/router/basic-handler.go
index b973d01..68d305c 100644
--- a/internal/router/basic-handler.go
+++ b/internal/router/basic-handler.go
@@ -42,6 +42,10 @@ func (r *BasicHandler) ContentType() string {
return fiber.MIMETextHTMLCharsetUTF8
}
+func (r *BasicHandler) RateLimiter() *fiber.Handler {
+ return nil
+}
+
func (r *BasicHandler) Render(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
if !r.IsTemplated() {
panic("handler did not implement Render method (while being non-templated)")
diff --git a/internal/router/handlers/api-v1-blog-search.go b/internal/router/handlers/api-v1-blog-search.go
index c9a91ad..38004a1 100644
--- a/internal/router/handlers/api-v1-blog-search.go
+++ b/internal/router/handlers/api-v1-blog-search.go
@@ -44,6 +44,10 @@ func (r *BlogSearchHandler) ToValidateLang() router.LangSetting {
return router.InForm
}
+func (r *BlogSearchHandler) RateLimiter() *fiber.Handler {
+ return &router.RateLimiterLoose
+}
+
func (r *BlogSearchHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
sort := c.Query("sort")
tz := c.Query("tz")
diff --git a/internal/router/handlers/api-v1-email-send-verification-code.go b/internal/router/handlers/api-v1-email-send-verification-code.go
index 20c5fb6..0b53efa 100644
--- a/internal/router/handlers/api-v1-email-send-verification-code.go
+++ b/internal/router/handlers/api-v1-email-send-verification-code.go
@@ -38,6 +38,10 @@ func (r *SendVerificationCodeHandler) ToValidateLang() router.LangSetting {
return router.InReferer
}
+func (r *SendVerificationCodeHandler) RateLimiter() *fiber.Handler {
+ return &router.RateLimiterStrict
+}
+
func (r *SendVerificationCodeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
id := c.IP()
templateMap["StatusId"] = "email-message"
diff --git a/internal/router/handlers/api-v1-email-verify.go b/internal/router/handlers/api-v1-email-verify.go
index c9ec969..ace1870 100644
--- a/internal/router/handlers/api-v1-email-verify.go
+++ b/internal/router/handlers/api-v1-email-verify.go
@@ -36,6 +36,10 @@ func (r *VerifyCodeHandler) ToValidateLang() router.LangSetting {
return router.InReferer
}
+func (r *VerifyCodeHandler) RateLimiter() *fiber.Handler {
+ return &router.RateLimiterStrict
+}
+
func (r *VerifyCodeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
verificationCode := c.FormValue("email_code")
templateMap["StatusId"] = "verification-message"
diff --git a/internal/router/handlers/api-v1-like-put.go b/internal/router/handlers/api-v1-like-put.go
index 434f15a..8305d2b 100644
--- a/internal/router/handlers/api-v1-like-put.go
+++ b/internal/router/handlers/api-v1-like-put.go
@@ -37,6 +37,10 @@ func (r *PutLikeHandler) ToValidateLang() router.LangSetting {
return router.InReferer
}
+func (r *PutLikeHandler) RateLimiter() *fiber.Handler {
+ return &router.RateLimiterMedium
+}
+
func (r *PutLikeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
path, pathParts, _, err := router.GetPathFromReferer(c)
if err != nil {
diff --git a/internal/router/handlers/api-v1-subs-put.go b/internal/router/handlers/api-v1-subs-put.go
index 144abe5..32fba7e 100644
--- a/internal/router/handlers/api-v1-subs-put.go
+++ b/internal/router/handlers/api-v1-subs-put.go
@@ -34,6 +34,10 @@ func (r *PutSubsHandler) ToValidateLang() router.LangSetting {
return router.InReferer
}
+func (r *PutSubsHandler) RateLimiter() *fiber.Handler {
+ return &router.RateLimiterMedium
+}
+
func (r *PutSubsHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
templateMap["StatusId"] = "subs-message"
diff --git a/internal/router/rate-limiters.go b/internal/router/rate-limiters.go
new file mode 100644
index 0000000..b43581d
--- /dev/null
+++ b/internal/router/rate-limiters.go
@@ -0,0 +1,37 @@
+package router
+
+import (
+ "time"
+
+ "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/fiber/v2/middleware/limiter"
+)
+
+var (
+ RateLimiterStrict = limiter.New(limiter.Config{
+ Max: 5,
+ Expiration: time.Minute,
+ KeyGenerator: func(c *fiber.Ctx) string { return c.IP() },
+ LimitReached: func(c *fiber.Ctx) error {
+ return c.Status(fiber.StatusTooManyRequests).SendString("rate limit")
+ },
+ })
+
+ RateLimiterMedium = limiter.New(limiter.Config{
+ Max: 30,
+ Expiration: time.Minute,
+ KeyGenerator: func(c *fiber.Ctx) string { return c.IP() },
+ LimitReached: func(c *fiber.Ctx) error {
+ return c.Status(fiber.StatusTooManyRequests).SendString("rate limit")
+ },
+ })
+
+ RateLimiterLoose = limiter.New(limiter.Config{
+ Max: 60,
+ Expiration: time.Minute,
+ KeyGenerator: func(c *fiber.Ctx) string { return c.IP() },
+ LimitReached: func(c *fiber.Ctx) error {
+ return c.Status(fiber.StatusTooManyRequests).SendString("rate limit")
+ },
+ })
+)
diff --git a/internal/router/router.go b/internal/router/router.go
index 926d21a..5230e74 100644
--- a/internal/router/router.go
+++ b/internal/router/router.go
@@ -78,6 +78,7 @@ type Route interface {
TemplatesToInject() []string
SitemapInfo(supplements *Supplements) []SitemapInfo
ContentType() string
+ RateLimiter() *fiber.Handler
Render(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error)
AddMeta(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (meta []MetaField, err error)
AddLinkedData(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (ld map[string]any, err error)
@@ -267,6 +268,10 @@ func (r *Router) InitRoutes() (err error) {
return fmt.Errorf("failed to add '%s %s' route into template manager: %w", method, match, err)
}
+ if rateLimiter := route.RateLimiter(); rateLimiter != nil {
+ r.app.Use(match, *rateLimiter)
+ }
+
if route.IsTemplated() {
if _, ok := r.templatedRoutes[method]; !ok {
r.templatedRoutes[method] = make(map[string]Route)