Files
jiang13-bbs/backend/middleware/visit_capture.go
freefire 7b327bc8cc feat: 站点访问统计与可配置侧边栏组件
- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面
- 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件
- 新增 widget 嵌入代码生成(widgetCode)与 widgetApi
- 遥测逻辑迁移至 handler 层,删除 service/telemetry
- env 示例补充 Docker 部署可信代理 IP 说明
2026-09-24 18:53:45 +08:00

63 lines
1.6 KiB
Go
Raw 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 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)
}
}