- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
63 lines
1.6 KiB
Go
63 lines
1.6 KiB
Go
package middleware
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"github.com/freefire/jiang13-bbs/model"
|
||
"github.com/freefire/jiang13-bbs/service"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// VisitCapture 服务端访问捕获:只记「UA 命中爬虫库」与「响应状态 ≥ 400」两类请求,
|
||
// 普通人类浏览由客户端埋点上报(互补、不重复)。事件非阻塞入队,不影响响应。
|
||
func VisitCapture(svc *service.VisitStatsService) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
c.Next()
|
||
|
||
if svc == nil || !svc.Enabled() {
|
||
return
|
||
}
|
||
// 静态资源与健康检查/WS 无统计意义
|
||
path := c.Request.URL.Path
|
||
if strings.HasPrefix(path, "/uploads") || path == "/health" || path == "/api/ws" {
|
||
return
|
||
}
|
||
|
||
ua := c.Request.UserAgent()
|
||
bot := service.ClassifyBot(ua)
|
||
status := c.Writer.Status()
|
||
if bot == "" && status < 400 {
|
||
return // 正常人类请求:不记
|
||
}
|
||
|
||
ev := model.VisitEvent{}
|
||
if bot != "" {
|
||
ev.Kind = model.VisitKindBot
|
||
ev.Bot = bot
|
||
} else {
|
||
ev.Kind = model.VisitKindProbe
|
||
ev.Status = status
|
||
}
|
||
if q := c.Request.URL.RawQuery; q != "" {
|
||
ev.Path = path + "?" + q
|
||
} else {
|
||
ev.Path = path
|
||
}
|
||
if len(ev.Path) > 512 {
|
||
ev.Path = ev.Path[:512]
|
||
}
|
||
ev.IP = service.CanonicalIP(c.ClientIP())
|
||
if vid, err := c.Cookie(service.VisitorCookieName); err == nil && service.ValidVisitorID(vid) {
|
||
ev.VidHash = service.HashVisitorID(vid)
|
||
}
|
||
if u := CurrentUser(c); u != nil {
|
||
ev.UserID = u.ID
|
||
}
|
||
p := service.ParseUserAgent(ua)
|
||
ev.Device = p.Kind
|
||
ev.OS = p.OS
|
||
ev.Browser = p.Browser
|
||
svc.Enqueue(ev)
|
||
}
|
||
}
|