79 lines
1.6 KiB
Go
79 lines
1.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"gotest/internal/geoip"
|
|
"gotest/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// responseWriter 包装 gin.ResponseWriter 以捕获状态码
|
|
type responseWriter struct {
|
|
gin.ResponseWriter
|
|
statusCode int
|
|
}
|
|
|
|
func (w *responseWriter) WriteHeader(code int) {
|
|
w.statusCode = code
|
|
w.ResponseWriter.WriteHeader(code)
|
|
}
|
|
|
|
// AccessLog 访问日志中间件,记录每次 HTTP 请求到数据库
|
|
func AccessLog(db *gorm.DB, ipService *geoip.Service) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// 跳过健康检查和静态资源
|
|
path := c.Request.URL.Path
|
|
if path == "/api/health" || strings.HasPrefix(path, "/shared/") || strings.HasPrefix(path, "/assets/") {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
start := time.Now()
|
|
clientIP := c.ClientIP()
|
|
|
|
// 包装响应写入器以捕获状态码
|
|
rw := &responseWriter{ResponseWriter: c.Writer, statusCode: http.StatusOK}
|
|
c.Writer = rw
|
|
|
|
c.Next()
|
|
|
|
// 异步写入日志(不阻塞请求响应)
|
|
go func() {
|
|
cost := int(time.Since(start).Milliseconds())
|
|
ua := c.Request.UserAgent()
|
|
if len(ua) > 500 {
|
|
ua = ua[:500]
|
|
}
|
|
referer := c.Request.Referer()
|
|
if len(referer) > 500 {
|
|
referer = referer[:500]
|
|
}
|
|
p := c.Request.URL.Path
|
|
if len(p) > 500 {
|
|
p = p[:500]
|
|
}
|
|
|
|
// 查询 IP 归属地
|
|
location := ipService.Lookup(clientIP)
|
|
|
|
log := model.AccessLog{
|
|
IP: clientIP,
|
|
Path: p,
|
|
Method: c.Request.Method,
|
|
UserAgent: ua,
|
|
Referer: referer,
|
|
Location: location,
|
|
Status: rw.statusCode,
|
|
Cost: cost,
|
|
}
|
|
|
|
db.Create(&log)
|
|
}()
|
|
}
|
|
}
|