chore: merge backend repo history into monorepo at backend/
git-subtree-dir: backend git-subtree-mainline:dc11190527git-subtree-split:acae976622
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"dating-app/internal/utils"
|
||||
)
|
||||
|
||||
type bucket struct {
|
||||
tokens float64
|
||||
lastSeenAt time.Time
|
||||
}
|
||||
|
||||
// RateLimiter یک rate limiter سادهی token-bucket بهازای هر IP؛ برای جلوگیری
|
||||
// از سوءاستفاده از اندپوینتهای حساس مثل درخواست OTP (که هزینهی پیامک داره
|
||||
// و هدف مناسبی برای اسپم/brute-force هست). maxTokens یعنی سقف درخواست
|
||||
// پشتسرهم مجاز، refillPerSecond یعنی سرعت شارژ دوبارهی سطل.
|
||||
func RateLimiter(maxTokens, refillPerSecond float64) gin.HandlerFunc {
|
||||
var mu sync.Mutex
|
||||
buckets := make(map[string]*bucket)
|
||||
|
||||
// سطلهای راکد (IP هایی که مدتیه درخواست نزدن) رو دورهای پاک میکنه تا
|
||||
// حافظه با تعداد IP های دیدهشده بینهایت رشد نکنه.
|
||||
go func() {
|
||||
ticker := time.NewTicker(10 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
cutoff := time.Now().Add(-10 * time.Minute)
|
||||
mu.Lock()
|
||||
for k, b := range buckets {
|
||||
if b.lastSeenAt.Before(cutoff) {
|
||||
delete(buckets, k)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
return func(c *gin.Context) {
|
||||
key := c.ClientIP()
|
||||
now := time.Now()
|
||||
|
||||
mu.Lock()
|
||||
b, ok := buckets[key]
|
||||
if !ok {
|
||||
b = &bucket{tokens: maxTokens, lastSeenAt: now}
|
||||
buckets[key] = b
|
||||
}
|
||||
elapsed := now.Sub(b.lastSeenAt).Seconds()
|
||||
b.tokens = min(maxTokens, b.tokens+elapsed*refillPerSecond)
|
||||
b.lastSeenAt = now
|
||||
|
||||
allowed := b.tokens >= 1
|
||||
if allowed {
|
||||
b.tokens--
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
if !allowed {
|
||||
utils.Fail(c, http.StatusTooManyRequests, "درخواستهای زیادی فرستادی؛ کمی صبر کن و دوباره امتحان کن", nil)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user