Merge pull request 'feature/ratelimitter' (#9) from feature/ratelimitter into dev
Reviewed-on: winter-access/backend_nam#9
This commit is contained in:
commit
94101e9ba4
|
|
@ -19,6 +19,7 @@ type BackboneController struct {
|
||||||
func (bc *BackboneController) Route() {
|
func (bc *BackboneController) Route() {
|
||||||
rg := bc.rg.Group("/backbone")
|
rg := bc.rg.Group("/backbone")
|
||||||
rg.Use(middleware.AuthMiddleware())
|
rg.Use(middleware.AuthMiddleware())
|
||||||
|
rg.Use(middleware.RateLimitMiddleware())
|
||||||
{
|
{
|
||||||
rg.GET("", bc.GetBackbone())
|
rg.GET("", bc.GetBackbone())
|
||||||
rg.POST("", bc.CreateBackbone())
|
rg.POST("", bc.CreateBackbone())
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type DevicePortController struct {
|
||||||
func (dc *DevicePortController) Route() {
|
func (dc *DevicePortController) Route() {
|
||||||
rg := dc.rg.Group("/device-port")
|
rg := dc.rg.Group("/device-port")
|
||||||
rg.Use(middleware.AuthMiddleware())
|
rg.Use(middleware.AuthMiddleware())
|
||||||
|
rg.Use(middleware.RateLimitMiddleware())
|
||||||
{
|
{
|
||||||
rg.GET("", dc.GetDevicePort())
|
rg.GET("", dc.GetDevicePort())
|
||||||
rg.POST("", dc.CreateDevicePort())
|
rg.POST("", dc.CreateDevicePort())
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type DeviceController struct {
|
||||||
func (dc *DeviceController) Route() {
|
func (dc *DeviceController) Route() {
|
||||||
rg := dc.rg.Group("/devices")
|
rg := dc.rg.Group("/devices")
|
||||||
rg.Use(middleware.AuthMiddleware())
|
rg.Use(middleware.AuthMiddleware())
|
||||||
|
rg.Use(middleware.RateLimitMiddleware())
|
||||||
{
|
{
|
||||||
rg.POST("", dc.CreateDevice())
|
rg.POST("", dc.CreateDevice())
|
||||||
rg.GET("", dc.GetAllDevices())
|
rg.GET("", dc.GetAllDevices())
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type FishboneController struct {
|
||||||
func (fc *FishboneController) Route() {
|
func (fc *FishboneController) Route() {
|
||||||
rg := fc.rg.Group("/fishbone")
|
rg := fc.rg.Group("/fishbone")
|
||||||
rg.Use(middleware.AuthMiddleware())
|
rg.Use(middleware.AuthMiddleware())
|
||||||
|
rg.Use(middleware.RateLimitMiddleware())
|
||||||
{
|
{
|
||||||
rg.GET("", fc.GetFishbone())
|
rg.GET("", fc.GetFishbone())
|
||||||
rg.POST("", fc.CreateFishbone())
|
rg.POST("", fc.CreateFishbone())
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type TowerController struct {
|
||||||
func (tc *TowerController) Route() {
|
func (tc *TowerController) Route() {
|
||||||
rg := tc.rg.Group("/tower")
|
rg := tc.rg.Group("/tower")
|
||||||
rg.Use(middleware.AuthMiddleware())
|
rg.Use(middleware.AuthMiddleware())
|
||||||
|
rg.Use(middleware.RateLimitMiddleware())
|
||||||
{
|
{
|
||||||
rg.GET("", tc.GetTower())
|
rg.GET("", tc.GetTower())
|
||||||
rg.POST("", tc.CreateTower())
|
rg.POST("", tc.CreateTower())
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"users_management/m/middleware"
|
||||||
"users_management/m/model/dto"
|
"users_management/m/model/dto"
|
||||||
"users_management/m/usecase"
|
"users_management/m/usecase"
|
||||||
"users_management/m/utils/common"
|
"users_management/m/utils/common"
|
||||||
|
|
@ -17,7 +18,10 @@ type UsersController struct {
|
||||||
|
|
||||||
func (uc *UsersController) Route() {
|
func (uc *UsersController) Route() {
|
||||||
rg:= uc.rg.Group("/users")
|
rg:= uc.rg.Group("/users")
|
||||||
rg.POST("/login", uc.Login())
|
rg.Use(middleware.RateLoginMiddleware())
|
||||||
|
{
|
||||||
|
rg.POST("/login", uc.Login())
|
||||||
|
}
|
||||||
rg.POST("/logout", uc.Logout())
|
rg.POST("/logout", uc.Logout())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -44,6 +44,7 @@ require (
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
|
golang.org/x/time v0.10.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -101,6 +101,8 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
|
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
"users_management/m/utils/common"
|
"users_management/m/utils/common"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
@ -50,6 +52,15 @@ func AuthMiddleware() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var authResponse res.AuthMeResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&authResponse); err != nil {
|
||||||
|
common.ErrorResponses(c, http.StatusInternalServerError, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("userID", authResponse.Data.NomorInduk)
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
"users_management/m/utils/common"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rateLimiters = make(map[string]*rate.Limiter)
|
||||||
|
mu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func getRateLimiter(userID string) *rate.Limiter {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
limiter, exists := rateLimiters[userID]
|
||||||
|
if !exists {
|
||||||
|
limiter = rate.NewLimiter(rate.Every(1*time.Minute), 50) // 1 request per second with a burst of 2 requests
|
||||||
|
rateLimiters[userID] = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
return limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoginLimiter() *rate.Limiter {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
limiter, exists := rateLimiters["login"]
|
||||||
|
if !exists {
|
||||||
|
limiter = rate.NewLimiter(rate.Every(1*time.Minute), 4) // 5 request per second with a burst of 10 requests
|
||||||
|
rateLimiters["login"] = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
return limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
func RateLimitMiddleware() gin.HandlerFunc{
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
userID, exists := c.Get("userID")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(c, http.StatusUnauthorized, "Unauthorized: No user ID found")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
limiter := getRateLimiter(userID.(string))
|
||||||
|
|
||||||
|
if !limiter.Allow() {
|
||||||
|
common.ErrorResponses(c, http.StatusTooManyRequests, "Too many requests")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RateLoginMiddleware() gin.HandlerFunc{
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
limiter := getLoginLimiter()
|
||||||
|
|
||||||
|
if !limiter.Allow() {
|
||||||
|
common.ErrorResponses(c, http.StatusTooManyRequests, "Too many requests")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue