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
+105
View File
@@ -0,0 +1,105 @@
package limitHandler
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/httputil"
"91porn-server/common/stderr"
"91porn-server/web/webg"
"github.com/gin-gonic/gin"
"github.com/patrickmn/go-cache"
)
var limitURI = []string{
"/api/web/admin/login",
"/api/web/admin/slogin",
"/api/web/admin/verify",
}
var (
tgBotToken = "2013237388:AAFwvKvEmQfEvcI7wt_64Zkp9KPfgHo8sow"
tgGroupId = "-1003735177636"
)
var (
limitDuration = 3 * time.Minute
limitCount = 5
)
var memCache = cache.New(1*time.Minute, 10*time.Minute)
// LoginLimit 登陆频率限制
func LoginLimit(ctx *gin.Context) {
path := ctx.Request.URL.Path
// 检查是否需要限制频率
check := false
for _, p := range limitURI {
if strings.HasPrefix(path, p) {
check = true
}
}
if !check {
return
}
// 测试环境不限制
if webg.Conf.Base.Env != constant.ProdEnv {
return
}
// 获取KEY:只按 path + 管理员账号(name),不含 IP
// (对方会伪造 IP,若 key 含 IP 则每次换 IP 就重置计数,导致限流被绕过一直重试)
ip := ctx.ClientIP() // ip 仅用于超限预警展示,不参与限流 key
key := fmt.Sprintf("web-limit:%s", path)
// 检查参数值
var params map[string]interface{}
if ctx.Request.Method == http.MethodPost {
raw, _ := ctx.GetRawData()
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(raw))
if err := json.Unmarshal(raw, &params); err == nil {
if a, ok := params["name"]; ok { // 做登陆频率限制只检查管理员账号
key += ":" + a.(string)
}
}
}
// 检查频率
aCount, ok := memCache.Get(key)
if !ok {
memCache.Set(key, 1, limitDuration)
return
}
count, ok2 := aCount.(int)
if !ok2 {
memCache.Set(key, 1, limitDuration)
return
}
count++
defer func() { memCache.Set(key, count, limitDuration) }()
if count > limitCount {
// 超出限制
go func() {
// 预警
message := fmt.Sprintf("[91Porn]\n登陆请求频率过高!\n时间: %s\n请求地址: %s\n请求参数: %+v\nIP: %s\n错误次数: %d", time.Now().Format("2006-01-02 15:04:05"), path, params, ip, count)
// 请求地址/参数为不可信输入,去掉非法 UTF-8 字节;不用 parse_mode,避免 Markdown 把内容解析成实体后报错
message = strings.ToValidUTF8(message, "")
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", tgBotToken)
req := map[string]interface{}{
"chat_id": tgGroupId,
"text": message,
}
resp, _ := httputil.DefaultClientPostJson(url, nil, req)
_ = resp.Body.Close()
}()
ctx.Abort()
common.ServeJSON(ctx, stderr.ErrReqForbidden, nil)
return
}
ctx.Next()
}