diff --git a/backend/frontend/frontend_excluded.go b/backend/frontend/frontend_excluded.go index 19ab84ad..2e0e35ef 100644 --- a/backend/frontend/frontend_excluded.go +++ b/backend/frontend/frontend_excluded.go @@ -4,6 +4,6 @@ package frontend import "github.com/gin-gonic/gin" -func RegisterFrontend(router *gin.Engine) error { +func RegisterFrontend(router *gin.Engine, rateLimitMiddleware gin.HandlerFunc) error { return ErrFrontendNotIncluded } diff --git a/backend/frontend/frontend_included.go b/backend/frontend/frontend_included.go index dcf346aa..ac1f6b2e 100644 --- a/backend/frontend/frontend_included.go +++ b/backend/frontend/frontend_included.go @@ -52,7 +52,7 @@ func init() { } } -func RegisterFrontend(router *gin.Engine) error { +func RegisterFrontend(router *gin.Engine, rateLimitMiddleware gin.HandlerFunc) error { distFS, err := fs.Sub(frontendFS, "dist") if err != nil { return fmt.Errorf("failed to create sub FS: %w", err) @@ -61,7 +61,7 @@ func RegisterFrontend(router *gin.Engine) error { cacheMaxAge := time.Hour * 24 fileServer := NewFileServerWithCaching(http.FS(distFS), int(cacheMaxAge.Seconds())) - router.NoRoute(func(c *gin.Context) { + handler := func(c *gin.Context) { path := strings.TrimPrefix(c.Request.URL.Path, "/") if strings.HasSuffix(path, "/") { @@ -97,7 +97,9 @@ func RegisterFrontend(router *gin.Engine) error { // Serve other static assets with caching c.Request.URL.Path = "/" + path fileServer.ServeHTTP(c.Writer, c.Request) - }) + } + + router.NoRoute(rateLimitMiddleware, handler) return nil } diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 68ae066e..8078b619 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -53,8 +53,6 @@ func initRouter(db *gorm.DB, svc *services) (utils.Service, error) { r.Use(otelgin.Middleware(common.Name)) } - rateLimitMiddleware := middleware.NewRateLimitMiddleware().Add(rate.Every(time.Second), 60) - // Setup global middleware r.Use(middleware.HeadMiddleware()) r.Use(middleware.NewCacheControlMiddleware().Add()) @@ -62,7 +60,8 @@ func initRouter(db *gorm.DB, svc *services) (utils.Service, error) { r.Use(middleware.NewCspMiddleware().Add()) r.Use(middleware.NewErrorHandlerMiddleware().Add()) - err := frontend.RegisterFrontend(r) + frontendRateLimitMiddleware := middleware.NewRateLimitMiddleware().Add(rate.Every(100*time.Millisecond), 300) + err := frontend.RegisterFrontend(r, frontendRateLimitMiddleware) if errors.Is(err, frontend.ErrFrontendNotIncluded) { slog.Warn("Frontend is not included in the build. Skipping frontend registration.") } else if err != nil { @@ -73,8 +72,10 @@ func initRouter(db *gorm.DB, svc *services) (utils.Service, error) { authMiddleware := middleware.NewAuthMiddleware(svc.apiKeyService, svc.userService, svc.jwtService) fileSizeLimitMiddleware := middleware.NewFileSizeLimitMiddleware() + apiRateLimitMiddleware := middleware.NewRateLimitMiddleware().Add(rate.Every(time.Second), 100) + // Set up API routes - apiGroup := r.Group("/api", rateLimitMiddleware) + apiGroup := r.Group("/api", apiRateLimitMiddleware) controller.NewApiKeyController(apiGroup, authMiddleware, svc.apiKeyService) controller.NewWebauthnController(apiGroup, authMiddleware, middleware.NewRateLimitMiddleware(), svc.webauthnService, svc.appConfigService) controller.NewOidcController(apiGroup, authMiddleware, fileSizeLimitMiddleware, svc.oidcService, svc.jwtService) @@ -96,7 +97,7 @@ func initRouter(db *gorm.DB, svc *services) (utils.Service, error) { } // Set up base routes - baseGroup := r.Group("/", rateLimitMiddleware) + baseGroup := r.Group("/", apiRateLimitMiddleware) controller.NewWellKnownController(baseGroup, svc.jwtService) // Set up healthcheck routes