🎨新增请求日志记录

This commit is contained in:
coward 2024-06-03 17:09:45 +08:00
parent 30b7d6aeb6
commit e1f168b274
14 changed files with 266 additions and 6 deletions

42
http/api/dashboard.go Normal file
View File

@ -0,0 +1,42 @@
package api
import (
"github.com/gin-gonic/gin"
"wireguard-dashboard/http/param"
"wireguard-dashboard/model/entity"
"wireguard-dashboard/repository"
"wireguard-dashboard/utils"
)
type dashboard struct{}
func Dashboard() dashboard {
return dashboard{}
}
// List
// @description: 操作日志分页列表
// @receiver d
// @param c
func (d dashboard) List(c *gin.Context) {
var p param.OnlyPage
if err := c.ShouldBind(&p); err != nil {
utils.GinResponse(c).FailedWithErr("参数错误", err)
return
}
// 如果不是超级管理员只能看自己的
userInfo, ok := c.Get("user")
if !ok {
utils.GinResponse(c).AuthorizationFailed()
return
}
data, count, err := repository.SystemLog().List(p, userInfo.(*entity.User))
if err != nil {
utils.GinResponse(c).FailedWithErr("获取失败", err)
return
}
utils.GinResponse(c).OkWithPage(data, count, p.Current, p.Size)
}

View File

@ -4,3 +4,7 @@ type page struct {
Current int `json:"current" form:"current" binding:"required"`
Size int `json:"size" form:"size" binding:"required"`
}
type OnlyPage struct {
page
}

View File

@ -26,6 +26,7 @@ func main() {
route.ServerApi,
route.ClientApi,
route.SettingApi,
route.DashboardApi,
)
handler := route.InitRouter()

107
middleware/system_log.go Normal file
View File

@ -0,0 +1,107 @@
package middleware
import (
"bytes"
"gitee.ltd/lxh/logger/log"
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"io"
"regexp"
"strings"
"time"
"wireguard-dashboard/model/entity"
"wireguard-dashboard/repository"
)
// bodyWriter
// @description: 重写ResponseBody
type bodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func SystemLogRequest() gin.HandlerFunc {
return func(c *gin.Context) {
var userId string
if userInfo, ok := c.Get("user"); ok {
userId = userInfo.(*entity.User).Id
}
// 开始时间
start := time.Now()
host := c.Request.Host // 请求域名
path := c.Request.URL.Path // 接口地址
query := c.Request.URL.RawQuery // 参数
if strings.Contains(path, "/api/dashboard/list") {
c.Next()
return
}
var bodyStr string
if !strings.Contains(path, "/api/login") {
body, err := c.GetRawData() // body参数
if err == nil {
bodyStr = string(body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
reg := regexp.MustCompile("\\s+")
bodyStr = reg.ReplaceAllString(bodyStr, "")
}
}
method := c.Request.Method // 请求方式
ip := c.ClientIP() // 取出IP
// 处理实际客户端IP
if c.Request.Header.Get("U-Real-Ip") != "" {
ip = c.Request.Header.Get("U-Real-Ip") // 这个是网关Nginx自定义的Header头
} else if c.Request.Header.Get("U-Forwarded-For") != "" {
ip = c.Request.Header.Get("U-Forwarded-For") // 这个是网关Nginx自定义的Header头
}
ua := c.Request.UserAgent() // UA
// 重写客户端IP
c.Request.Header.Set("X-Forwarded-For", ip)
c.Request.Header.Set("X-Real-Ip", ip)
// 拦截response
bw := &bodyWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = bw
header := c.Request.Header
headerStr, _ := jsoniter.MarshalToString(header)
// 执行下一步请求
c.Next()
// 计算耗时
cost := time.Since(start).Milliseconds()
// 组装实体
l := entity.SystemLog{
UserId: userId,
ClientIP: ip,
Host: host,
Method: method,
Uri: path,
Header: headerStr,
Body: bodyStr,
Form: "",
Query: query,
UserAgent: ua,
Cost: cost,
StatusCode: c.Writer.Status(),
Response: "",
}
// 如果不是返回200把返回值保存一下
if c.Writer.Status() != 200 {
resp := bw.body.String()
l.Response = resp
}
go func() {
if er := repository.SystemLog().SaveLog(&l); er != nil {
log.Debugf("请求日志: %+v", l)
log.Errorf("保存请求日志失败: %v", er)
}
}()
}
}

View File

@ -10,3 +10,26 @@ type Setting struct {
func (Setting) TableName() string {
return "t_setting"
}
// SystemLog
// @description: 系统日志
type SystemLog struct {
Base
UserId string `json:"userId" gorm:"type:char(40);comment:'用户id'"`
ClientIP string `json:"clientIP" gorm:"type:varchar(60);not null;comment:'客户端IP'"`
Host string `json:"host" gorm:"type:varchar(255);not null;comment:'请求域名'"`
Method string `json:"method" gorm:"type:varchar(20);not null;comment:'请求方法[GET POST DELETE PUT PATCH]'"`
Uri string `json:"uri" gorm:"type:varchar(255);not null;comment:'请求path'"`
Header string `json:"header" gorm:"type:text;comment:'请求头'"`
Body string `json:"body" gorm:"type:text;comment:'请求体'"`
Form string `json:"form" gorm:"type:text;comment:'请求表单'"`
Query string `json:"query" gorm:"type:text;comment:'请求query'"`
UserAgent string `json:"userAgent" gorm:"type:text;comment:'ua信息'"`
Cost int64 `json:"cost" gorm:"type:int(10);comment:'请求耗时'"`
StatusCode int `json:"statusCode" gorm:"type:int(10);comment:'响应状态码'"`
Response string `json:"response" gorm:"type:text;comment:'返回数据'"`
}
func (SystemLog) TableName() string {
return "t_system_log"
}

14
model/vo/dashboard.go Normal file
View File

@ -0,0 +1,14 @@
package vo
import "wireguard-dashboard/model/entity"
type SystemLogItem struct {
Id string `json:"id"`
Username string `json:"username"`
ClientIP string `json:"clientIP"`
Method string `json:"method"`
Host string `json:"host"`
Uri string `json:"uri"`
StatusCode int `json:"statusCode"`
CreatedAt entity.JsonTime `json:"createdAt"`
}

50
repository/system_log.go Normal file
View File

@ -0,0 +1,50 @@
package repository
import (
"gorm.io/gorm"
"wireguard-dashboard/client"
"wireguard-dashboard/constant"
"wireguard-dashboard/http/param"
"wireguard-dashboard/model/entity"
"wireguard-dashboard/model/vo"
"wireguard-dashboard/utils"
)
type systemLog struct {
*gorm.DB
}
func SystemLog() systemLog {
return systemLog{
client.DB,
}
}
// SaveLog
// @description: 保存记录
// @receiver systemLog
// @param data
// @return err
func (s systemLog) SaveLog(data *entity.SystemLog) (err error) {
return s.Create(&data).Error
}
// List
// @description: 分页列表
// @receiver s
// @param p
// @return data
// @return total
// @return err
func (s systemLog) List(p param.OnlyPage, loginUser *entity.User) (data []vo.SystemLogItem, total int64, err error) {
sel := s.Scopes(utils.Page(p.Current, p.Size)).Table("t_system_log as tsl").
Joins("LEFT JOIN t_user as tu ON tu.id = tsl.user_id").
Select("tsl.id", "tu.name as username", "tsl.client_ip", "tsl.method", "tsl.status_code", "tsl.host", "tsl.uri", "tsl.created_at").
Order("tsl.created_at DESC")
if loginUser.IsAdmin == constant.NormalAdmin {
sel.Where("tsl.user_id = ?", loginUser.Id)
}
err = sel.Find(&data).Offset(-1).Limit(-1).Count(&total).Error
return
}

View File

@ -3,13 +3,14 @@ package route
import (
"github.com/gin-gonic/gin"
"wireguard-dashboard/http/api"
"wireguard-dashboard/middleware"
)
// CaptchaApi
// @description: 验证码
// @param r
func CaptchaApi(r *gin.RouterGroup) {
captcha := r.Group("captcha")
captcha := r.Group("captcha", middleware.SystemLogRequest())
{
captcha.GET("", api.Captcha().GenerateCaptcha) // 生成验证码
}

View File

@ -7,7 +7,7 @@ import (
)
func ClientApi(r *gin.RouterGroup) {
apiGroup := r.Group("client", middleware.Authorization())
apiGroup := r.Group("client", middleware.Authorization(), middleware.SystemLogRequest())
{
apiGroup.GET("list", api.Client().List) // 客户端列表
apiGroup.POST("save", middleware.Permission(), api.Client().Save) // 新增/编辑客户端

17
route/dashboard.go Normal file
View File

@ -0,0 +1,17 @@
package route
import (
"github.com/gin-gonic/gin"
"wireguard-dashboard/http/api"
"wireguard-dashboard/middleware"
)
// DashboardApi
// @description: 控制台相关API
// @param r
func DashboardApi(r *gin.RouterGroup) {
apiGroup := r.Group("dashboard", middleware.Authorization(), middleware.SystemLogRequest())
{
apiGroup.GET("list", api.Dashboard().List) // 操作日志
}
}

View File

@ -7,7 +7,7 @@ import (
)
func ServerApi(r *gin.RouterGroup) {
apiGroup := r.Group("server", middleware.Authorization())
apiGroup := r.Group("server", middleware.Authorization(), middleware.Permission(), middleware.SystemLogRequest())
{
apiGroup.GET("", api.Server().GetServer) // 获取服务端信息
apiGroup.POST("", middleware.Permission(), api.Server().SaveServer) // 新增/更新服务端信息

View File

@ -10,7 +10,7 @@ import (
// @description: 设置相关API
// @param r
func SettingApi(r *gin.RouterGroup) {
apiGroup := r.Group("setting", middleware.Authorization())
apiGroup := r.Group("setting", middleware.Authorization(), middleware.SystemLogRequest())
{
apiGroup.POST("save", middleware.Permission(), api.Setting().SetSetting) // 添加/更改设置 - 设置其他的配置
apiGroup.POST("server-global", middleware.Permission(), api.Setting().SetServerGlobal) // 设置服务端全局配置

View File

@ -8,13 +8,13 @@ import (
func UserApi(r *gin.RouterGroup) {
// 登陆相关API
login := r.Group("/login")
login := r.Group("/login", middleware.SystemLogRequest())
{
login.POST("", api.UserApi().Login)
}
// 用户登陆后相关的API
userApi := r.Group("user", middleware.Authorization())
userApi := r.Group("user", middleware.Authorization(), middleware.SystemLogRequest())
{
userApi.DELETE("logout", api.UserApi().Logout) // 用户退出登陆
userApi.GET("", api.UserApi().GetUser) // 获取登陆用户信息

View File

@ -47,6 +47,7 @@ func (s Script) DBMigrate() error {
new(entity.Server),
new(entity.Client),
new(entity.Setting),
new(entity.SystemLog),
}
return client.DB.AutoMigrate(ent...)