Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

104 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}
}
}