package ipblock import ( "net/http" "time" "91porn-server/app/appg" "91porn-server/common" "91porn-server/common/constant" "91porn-server/common/constant/redisconst" "91porn-server/common/stderr" "91porn-server/models/v/ipblockmod" "github.com/gin-gonic/gin" ) func IPBlock(blockType string) gin.HandlerFunc { return func(ctx *gin.Context) { if !appg.ShouldEnforceIPRateLimit() { return } ip := common.GetIP(ctx) key := redisconst.IPBlockKey(blockType) if count, err := appg.Redis.SCard(key); count == 0 || err != nil { ipdata, err := ipblockmod.AggregateForIPArray(blockType) if err == nil && len(ipdata.IPS) > 0 { // 仅在集合为空时回源重建:直接 SAdd,去掉 Del 以消除"先删后加"的空窗期漏拦; // TTL 设在 key 自身(此前误用 IPBlockKey(key) 二次包裹成 ip:block:ip:block:xxx, // 导致真实集合永不过期、永不回源刷新) _, _ = appg.Redis.SAdd(key, ipdata.IPS) _, _ = appg.Redis.ExpireKey(key, redisconst.IPBlockExpire) } } isBlock, err := appg.Redis.SISMember(key, ip) //表示当前IP在限制名单中 if isBlock && err == nil { ctx.AbortWithStatusJSON(http.StatusOK, gin.H{ "code": stderr.ErrReqForbidden, "msg": stderr.ErrReqForbidden.Msg(), "tip": stderr.ErrReqForbidden.Tip(), }) return } } } func IPAutoBlock(blockType string, duration time.Duration, count int64) gin.HandlerFunc { return func(ctx *gin.Context) { if !appg.ShouldEnforceIPRateLimit() { return } ip := common.GetIP(ctx) if ip != "" { key := redisconst.IPAutoBlockKey(blockType, ip) cnt := appg.Redis.Incr(key) if cnt == 1 { _, _ = appg.Redis.ExpireKey(key, duration) } if cnt >= count { switch blockType { case constant.Register: ctx.AbortWithStatusJSON(http.StatusOK, gin.H{ "code": stderr.ErrLoginTooFrequently, "msg": stderr.ErrLoginTooFrequently.Msg(), "tip": stderr.ErrLoginTooFrequently.Tip(), }) default: ctx.AbortWithStatusJSON(http.StatusOK, gin.H{ "code": stderr.ErrReqForbidden, "msg": stderr.ErrReqForbidden.Msg(), "tip": stderr.ErrReqForbidden.Tip(), }) } return } } } } func CountAutoBlock(blockType string, duration time.Duration, count int64) gin.HandlerFunc { return func(ctx *gin.Context) { var key string switch blockType { case constant.BlockComment: uid, err := common.GetUID(ctx) if err == nil { key = redisconst.AutoBlockKey(blockType, uid) } } cnt := appg.Redis.Incr(key) if cnt == 1 { _, _ = appg.Redis.ExpireKey(key, duration) } if cnt > count { ctx.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{ "code": stderr.ErrReqForbidden, "msg": stderr.ErrReqForbidden.Msg(), "tip": stderr.ErrReqForbidden.Tip(), }) return } } }