Merge pull request 'feature/ratelimitter' (#9) from feature/ratelimitter into dev

Reviewed-on: winter-access/backend_nam#9
This commit is contained in:
areeqakbr 2025-02-19 01:42:16 +00:00
commit 94101e9ba4
10 changed files with 102 additions and 2 deletions

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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.Use(middleware.RateLoginMiddleware())
{
rg.POST("/login", uc.Login()) rg.POST("/login", uc.Login())
}
rg.POST("/logout", uc.Logout()) rg.POST("/logout", uc.Logout())
} }

1
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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()
} }

View File

@ -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()
}
}