Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
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
}
}
}