ulflow_phattt2901 9df4673657
Some checks failed
CI Pipeline / Security Scan (push) Failing after 4m42s
CI Pipeline / Lint (push) Failing after 5m1s
CI Pipeline / Test (push) Has been skipped
CI Pipeline / Build (push) Has been skipped
CI Pipeline / Notification (push) Successful in 2s
chore: Update config file
2025-05-25 15:22:23 +07:00

142 lines
3.7 KiB
Go

package middleware
import (
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
// RateLimiterConfig chứa cấu hình cho rate limiting
type RateLimiterConfig struct {
// Số request tối đa trong khoảng thời gian
Rate int `yaml:"rate"`
// Khoảng thời gian giữa các request
Window time.Duration `yaml:"window"`
// Danh sách các route được bỏ qua rate limiting
ExcludedRoutes []string `yaml:"excluded_routes"`
}
// IPRateLimiter quản lý rate limiting theo IP
type IPRateLimiter struct {
ips map[string]*rate.Limiter
mu *sync.RWMutex
r rate.Limit
b int
config RateLimiterConfig
excludedRoutes map[string]bool
}
// NewIPRateLimiter tạo mới một IPRateLimiter
func NewIPRateLimiter(r rate.Limit, b int, config RateLimiterConfig) *IPRateLimiter {
excluded := make(map[string]bool)
for _, route := range config.ExcludedRoutes {
excluded[route] = true
}
i := &IPRateLimiter{
ips: make(map[string]*rate.Limiter),
mu: &sync.RWMutex{},
r: r,
b: b,
config: config,
excludedRoutes: excluded,
}
// Clean up old limiters periodically
go i.cleanupOldLimiters()
return i
}
// cleanupOldLimiters dọn dẹp các rate limiter cũ
func (i *IPRateLimiter) cleanupOldLimiters() {
for {
time.Sleep(time.Hour) // Dọn dẹp mỗi giờ
i.mu.Lock()
now := time.Now()
for ip, limiter := range i.ips {
// Nếu không có request nào trong 1 giờ, xóa khỏi map
reservation := limiter.ReserveN(now, 0)
if reservation.Delay() == rate.InfDuration {
delete(i.ips, ip)
} else {
reservation.CancelAt(now) // Hủy reservation vì chúng ta chỉ kiểm tra
}
}
i.mu.Unlock()
}
}
// GetLimiter lấy hoặc tạo mới rate limiter cho IP
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
i.mu.Lock()
defer i.mu.Unlock()
limiter, exists := i.ips[ip]
if !exists {
limiter = rate.NewLimiter(i.r, i.b)
i.ips[ip] = limiter
}
return limiter
}
// Middleware tạo middleware rate limiting cho Gin
func (i *IPRateLimiter) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Bỏ qua rate limiting cho các route được chỉ định
if _, excluded := i.excludedRoutes[c.FullPath()]; excluded {
c.Next()
return
}
// Lấy IP của client (xử lý đằng sau proxy nếu cần)
ip := c.ClientIP()
// Lấy rate limiter cho IP này
limiter := i.GetLimiter(ip)
// Kiểm tra rate limit
if !limiter.Allow() {
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{
"status": "error",
"message": "Too many requests, please try again later.",
})
return
}
c.Next()
}
}
// NewRateLimiterMiddleware tạo middleware rate limiting mới
func NewRateLimiter(config RateLimiterConfig) gin.HandlerFunc {
// Chuyển đổi rate từ requests/giây sang rate.Limit
r := rate.Every(time.Second / time.Duration(config.Rate))
// Tạo rate limiter mới
limiter := NewIPRateLimiter(r, config.Rate, config)
// Trả về middleware
return limiter.Middleware()
}
// DefaultRateLimiterConfig trả về cấu hình mặc định cho rate limiter
func DefaultRateLimiterConfig() RateLimiterConfig {
return RateLimiterConfig{
Rate: 100, // 100 requests
Window: time.Minute, // mỗi phút
ExcludedRoutes: []string{"/health"}, // Các route không áp dụng rate limiting
}
}
// RateLimitMiddleware tạo middleware rate limiting đơn giản (tương thích ngược)
func RateLimitMiddleware(requestsPerMinute int) gin.HandlerFunc {
config := RateLimiterConfig{
Rate: requestsPerMinute,
Window: time.Minute,
}
return NewRateLimiter(config)
}