diff --git a/go.mod b/go.mod index 8c77bef..83e4dd7 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/goccy/go-json v0.10.2 github.com/gofiber/fiber/v2 v2.52.0 github.com/gofiber/template/jet/v2 v2.1.8 + github.com/im7mortal/kmutex v1.0.1 github.com/tidwall/gjson v1.17.0 golang.org/x/net v0.17.0 ) diff --git a/go.sum b/go.sum index 0149f99..c3c09d4 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/im7mortal/kmutex v1.0.1 h1:zAACzjwD+OEknDqnLdvRa/BhzFM872EBwKijviGLc9Q= +github.com/im7mortal/kmutex v1.0.1/go.mod h1:f71c/Ugk/+58OHRAgvgzPP3QEiWGUjK13fd8ozfKWdo= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= diff --git a/main.go b/main.go index 1967c3d..2987712 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( "github.com/gofiber/fiber/v2/middleware/recover" "github.com/gofiber/fiber/v2/utils" "github.com/gofiber/template/jet/v2" + "github.com/im7mortal/kmutex" ) func CanRequestSkipLimiter(c *fiber.Ctx) bool { @@ -88,6 +89,24 @@ func main() { }, }) + const limiterResetInterval time.Duration = 30 * time.Second + keyedSleepingSpot := kmutex.New() + server.Use(limiter.New(limiter.Config{ + Next: CanRequestSkipLimiter, + Expiration: limiterResetInterval, + Max: config.GlobalServerConfig.RequestLimit, + LimiterMiddleware: limiter.SlidingWindow{}, + LimitReached: func(c *fiber.Ctx) error { + // limit response throughput by pacing, since not every bot reads X-RateLimit-* + // on limit reached, they just have to wait + key := c.IP() + keyedSleepingSpot.Lock(key) + defer keyedSleepingSpot.Unlock(key) + time.Sleep(limiterResetInterval) + return c.Next() + }, + })) + server.Use(logger.New( logger.Config{ Format: "${time} ${ip} | ${path}\n", @@ -95,6 +114,10 @@ func main() { }, )) + server.Use(compress.New(compress.Config{ + Level: compress.LevelBestSpeed, // 1 + })) + if !config.GlobalServerConfig.InDevelopment { server.Use(cache.New( cache.Config{ @@ -116,27 +139,7 @@ func main() { }, )) } - - server.Use(recover.New(recover.Config{EnableStackTrace: config.GlobalServerConfig.InDevelopment})) - - server.Use(compress.New(compress.Config{ - Level: compress.LevelBestSpeed, // 1 - })) - - const limiterResetInterval time.Duration = 30 * time.Second - server.Use(limiter.New(limiter.Config{ - Next: CanRequestSkipLimiter, - Expiration: limiterResetInterval, - Max: config.GlobalServerConfig.RequestLimit, - LimiterMiddleware: limiter.SlidingWindow{}, - LimitReached: func(c *fiber.Ctx) error { - // limit response throughput by pacing, since not every bot reads X-RateLimit-* - // on limit reached, they just have to wait - time.Sleep(limiterResetInterval) - return c.Next() - }, - })) - + // Global HTTP headers server.Use(func(c *fiber.Ctx) error { c.Set("X-Frame-Options", "DENY") @@ -162,6 +165,8 @@ func main() { server.Static("/css/", "./views/css") server.Static("/js/", "./views/js") + server.Use(recover.New(recover.Config{EnableStackTrace: config.GlobalServerConfig.InDevelopment})) + // Routes server.Get("/", pages.IndexPage)