M accounts/routes.go => accounts/routes.go +6 -4
@@ 12,6 12,7 @@ import (
"git.sr.ht/~emersion/gqlclient"
sq "github.com/Masterminds/squirrel"
"github.com/labstack/echo/v4"
+ "github.com/labstack/echo/v4/middleware"
"netlandish.com/x/gobwebs"
formguard "netlandish.com/x/gobwebs-formguard"
"netlandish.com/x/gobwebs/accounts"
@@ 33,12 34,13 @@ type Service struct {
// RegisterRoutes ...
func (s *Service) RegisterRoutes() {
+ rlConfig := links.RLConfig
s.Group.GET("/register", s.Register).Name = s.RouteName("register")
- s.Group.POST("/register", s.Register).Name = s.RouteName("register_post")
+ s.Group.POST("/register", s.Register, middleware.RateLimiterWithConfig(rlConfig)).Name = s.RouteName("register_post")
s.Group.GET("/register/:key", s.Register).Name = s.RouteName("register_invitation")
- s.Group.POST("/register/:key", s.Register).Name = s.RouteName("register_invitation_post")
+ s.Group.POST("/register/:key", s.Register, middleware.RateLimiterWithConfig(rlConfig)).Name = s.RouteName("register_invitation_post")
s.Group.GET("/register/confirm", s.CompleteRegister).Name = s.RouteName("complete_register")
- s.Group.POST("/register/confirm", s.CompleteRegister).Name = s.RouteName("complete_register_post")
+ s.Group.POST("/register/confirm", s.CompleteRegister, middleware.RateLimiterWithConfig(rlConfig)).Name = s.RouteName("complete_register_post")
// Register default routes from gobwebs
gservice := accounts.NewService(s.Group, s.Name, s.fetch, s.RenderFunc)
@@ 330,7 332,7 @@ func (s *Service) CompleteRegister(c echo.Context) error {
}
return err
}
- messages.Success(c, lt.Translate("Register Completed"))
+ messages.Success(c, lt.Translate("Registration Completed"))
return c.Redirect(http.StatusMovedPermanently, c.Echo().Reverse("accounts:login"))
}
M helpers.go => helpers.go +24 -0
@@ 26,11 26,13 @@ import (
"github.com/99designs/gqlgen/graphql"
sq "github.com/Masterminds/squirrel"
"github.com/labstack/echo/v4"
+ "github.com/labstack/echo/v4/middleware"
"github.com/segmentio/ksuid"
"github.com/shopspring/decimal"
"golang.org/x/net/html"
"golang.org/x/text/cases"
"golang.org/x/text/language"
+ "golang.org/x/time/rate"
"netlandish.com/x/gobwebs"
"netlandish.com/x/gobwebs/config"
"netlandish.com/x/gobwebs/core"
@@ 1028,3 1030,25 @@ func GetSEOData(c echo.Context) *SEOData {
seoData.RssURL = url.String()
return seoData
}
+
+// RLConfig is a base rate limit config struct that can be used and altered
+// in handlers as needed.
+var RLConfig = middleware.RateLimiterConfig{
+ Skipper: func(c echo.Context) bool {
+ gctx := c.(*server.Context)
+ if gctx.User.IsAuthenticated() && gctx.User.IsSuperUser() {
+ return true
+ }
+ return false
+ },
+ Store: middleware.NewRateLimiterMemoryStoreWithConfig(
+ middleware.RateLimiterMemoryStoreConfig{
+ Rate: rate.Limit(3),
+ Burst: 5,
+ ExpiresIn: 3 * time.Minute,
+ },
+ ),
+ IdentifierExtractor: func(c echo.Context) (string, error) {
+ return c.RealIP(), nil
+ },
+}