all repos

onasty @ 36f59cd12d0992b16f20057c23cfe0e3c338e637

a one-time notes service
6 files changed, 51 insertions(+), 18 deletions(-)
fix: use separate ratelimiter for some endpoints (#166)

* feat: add slow rate limiter for the specific routes

* chore: update example .env

* fix(e2e): fix tests setup

* fixup! chore: update example .env
Author: Olexandr Smirnov ss2316544@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2025-07-15 14:03:33 +0300
Parent: d309c75
M .env.example
···
        50
        50
         RATELIMITER_RPS=100

      
        51
        51
         RATELIMITER_BURST=10

      
        52
        52
         RATELIMITER_TTL=3m

      
        
        53
        +

      
        
        54
        +SLOW_RATELIMITER_RPS=2

      
        
        55
        +SLOW_RATELIMITER_BURST=1

      
        
        56
        +SLOW_RATELIMITER_TTL=1m

      
M cmd/api/main.go
···
        124
        124
         		Burst: cfg.RateLimiterBurst,

      
        125
        125
         	}

      
        126
        126
         

      
        
        127
        +	slowRateLimiterConfig := ratelimit.Config{

      
        
        128
        +		RPS:   cfg.SlowRateLimiterRPS,

      
        
        129
        +		TTL:   cfg.SlowRateLimiterTTL,

      
        
        130
        +		Burst: cfg.SlowRateLimiterBurst,

      
        
        131
        +	}

      
        
        132
        +

      
        127
        133
         	handler := httptransport.NewTransport(

      
        128
        134
         		usersrv,

      
        129
        135
         		notesrv,

      ···
        132
        138
         		cfg.CORSAllowedOrigins,

      
        133
        139
         		cfg.CORSMaxAge,

      
        134
        140
         		rateLimiterConfig,

      
        
        141
        +		slowRateLimiterConfig,

      
        135
        142
         	)

      
        136
        143
         

      
        137
        144
         	// http server

      
M e2e/e2e_test.go
···
        143
        143
         		cfg.CORSAllowedOrigins,

      
        144
        144
         		cfg.CORSMaxAge,

      
        145
        145
         		ratelimitCfg,

      
        
        146
        +		ratelimitCfg,

      
        146
        147
         	)

      
        147
        148
         	e.router = handler.Handler()

      
        148
        149
         }

      
M internal/config/config.go
···
        60
        60
         	LogFormat   string

      
        61
        61
         	LogShowLine bool

      
        62
        62
         

      
        63
        
        -	RateLimiterRPS   int

      
        64
        
        -	RateLimiterBurst int

      
        65
        
        -	RateLimiterTTL   time.Duration

      
        
        63
        +	RateLimiterRPS       int

      
        
        64
        +	RateLimiterBurst     int

      
        
        65
        +	RateLimiterTTL       time.Duration

      
        
        66
        +	SlowRateLimiterRPS   int

      
        
        67
        +	SlowRateLimiterBurst int

      
        
        68
        +	SlowRateLimiterTTL   time.Duration

      
        66
        69
         }

      
        67
        70
         

      
        68
        71
         func NewConfig() *Config {

      ···
        116
        119
         		LogFormat:   getenvOrDefault("LOG_FORMAT", "json"),

      
        117
        120
         		LogShowLine: getenvOrDefault("LOG_SHOW_LINE", "true") == "true",

      
        118
        121
         

      
        119
        
        -		RateLimiterRPS:   mustGetenvOrDefaultInt("RATELIMITER_RPS", 100),

      
        120
        
        -		RateLimiterBurst: mustGetenvOrDefaultInt("RATELIMITER_BURST", 10),

      
        121
        
        -		RateLimiterTTL:   mustParseDuration(getenvOrDefault("RATELIMITER_TTL", "1m")),

      
        
        122
        +		RateLimiterRPS:       mustGetenvOrDefaultInt("RATELIMITER_RPS", 100),

      
        
        123
        +		RateLimiterBurst:     mustGetenvOrDefaultInt("RATELIMITER_BURST", 10),

      
        
        124
        +		RateLimiterTTL:       mustParseDuration(getenvOrDefault("RATELIMITER_TTL", "1m")),

      
        
        125
        +		SlowRateLimiterRPS:   mustGetenvOrDefaultInt("SLOW_RATELIMITER_RPS", 2),

      
        
        126
        +		SlowRateLimiterBurst: mustGetenvOrDefaultInt("SLOW_RATELIMITER_BURST", 2),

      
        
        127
        +		SlowRateLimiterTTL:   mustParseDuration(getenvOrDefault("SLOW_RATELIMITER_TTL", "1m")),

      
        122
        128
         	}

      
        123
        129
         }

      
        124
        130
         

      
M internal/transport/http/apiv1/apiv1.go
···
        5
        5
         	"github.com/olexsmir/onasty/internal/config"

      
        6
        6
         	"github.com/olexsmir/onasty/internal/service/notesrv"

      
        7
        7
         	"github.com/olexsmir/onasty/internal/service/usersrv"

      
        
        8
        +	"github.com/olexsmir/onasty/internal/transport/http/ratelimit"

      
        8
        9
         )

      
        9
        10
         

      
        10
        11
         type APIV1 struct {

      
        11
        
        -	usersrv usersrv.UserServicer

      
        12
        
        -	notesrv notesrv.NoteServicer

      
        13
        
        -	env     config.Environment

      
        14
        
        -	domain  string

      
        
        12
        +	usersrv          usersrv.UserServicer

      
        
        13
        +	notesrv          notesrv.NoteServicer

      
        
        14
        +	slowRatelimitCfg ratelimit.Config

      
        
        15
        +	env              config.Environment

      
        
        16
        +	domain           string

      
        15
        17
         }

      
        16
        18
         

      
        17
        19
         func NewAPIV1(

      
        18
        20
         	us usersrv.UserServicer,

      
        19
        21
         	ns notesrv.NoteServicer,

      
        
        22
        +	slowRatelimitCfg ratelimit.Config,

      
        20
        23
         	env config.Environment,

      
        21
        24
         	domain string,

      
        22
        25
         ) *APIV1 {

      
        23
        26
         	return &APIV1{

      
        24
        
        -		usersrv: us,

      
        25
        
        -		notesrv: ns,

      
        26
        
        -		env:     env,

      
        27
        
        -		domain:  domain,

      
        
        27
        +		usersrv:          us,

      
        
        28
        +		notesrv:          ns,

      
        
        29
        +		slowRatelimitCfg: slowRatelimitCfg,

      
        
        30
        +		env:              env,

      
        
        31
        +		domain:           domain,

      
        28
        32
         	}

      
        29
        33
         }

      
        30
        34
         

      ···
        36
        40
         		auth.POST("/signin", a.signInHandler)

      
        37
        41
         		auth.POST("/refresh-tokens", a.refreshTokensHandler)

      
        38
        42
         		auth.GET("/verify/:token", a.verifyHandler)

      
        39
        
        -		auth.POST("/resend-verification-email", a.resendVerificationEmailHandler)

      
        40
        
        -		auth.POST("/reset-password", a.requestResetPasswordHandler)

      
        
        43
        +		auth.POST("/resend-verification-email", a.slowRateLimit(), a.resendVerificationEmailHandler)

      
        
        44
        +		auth.POST("/reset-password", a.slowRateLimit(), a.requestResetPasswordHandler)

      
        41
        45
         		auth.POST("/reset-password/:token", a.resetPasswordHandler)

      
        42
        46
         

      
        43
        47
         		oauth := r.Group("/oauth")

      ···
        75
        79
         		}

      
        76
        80
         	}

      
        77
        81
         }

      
        
        82
        +

      
        
        83
        +func (a *APIV1) slowRateLimit() gin.HandlerFunc {

      
        
        84
        +	return ratelimit.MiddlewareWithConfig(a.slowRatelimitCfg)

      
        
        85
        +}

      
M internal/transport/http/http.go
···
        23
        23
         	corsAllowedOrigins []string

      
        24
        24
         	corsMaxAge         time.Duration

      
        25
        25
         	ratelimitCfg       ratelimit.Config

      
        
        26
        +	slowRatelimitCfg   ratelimit.Config

      
        26
        27
         }

      
        27
        28
         

      
        28
        29
         func NewTransport(

      ···
        33
        34
         	corsAllowedOrigins []string,

      
        34
        35
         	corsMaxAge time.Duration,

      
        35
        36
         	ratelimitCfg ratelimit.Config,

      
        
        37
        +	slowRatelimitCfg ratelimit.Config,

      
        36
        38
         ) *Transport {

      
        37
        39
         	return &Transport{

      
        38
        40
         		usersrv:            us,

      ···
        42
        44
         		corsAllowedOrigins: corsAllowedOrigins,

      
        43
        45
         		corsMaxAge:         corsMaxAge,

      
        44
        46
         		ratelimitCfg:       ratelimitCfg,

      
        
        47
        +		slowRatelimitCfg:   slowRatelimitCfg,

      
        45
        48
         	}

      
        46
        49
         }

      
        47
        50
         

      ···
        56
        59
         	)

      
        57
        60
         

      
        58
        61
         	api := r.Group("/api")

      
        59
        
        -	api.GET("/ping", t.pingHandler)

      
        60
        
        -	apiv1.NewAPIV1(t.usersrv, t.notesrv, t.env, t.domain).Routes(api.Group("/v1"))

      
        
        62
        +	{

      
        
        63
        +		api.GET("/ping", t.pingHandler)

      
        
        64
        +		apiv1.

      
        
        65
        +			NewAPIV1(t.usersrv, t.notesrv, t.slowRatelimitCfg, t.env, t.domain).

      
        
        66
        +			Routes(api.Group("/v1"))

      
        
        67
        +	}

      
        61
        68
         

      
        62
        69
         	return r.Handler()

      
        63
        70
         }