feat: 增加系统操作记录中间件

This commit is contained in:
ssongliu 2022-08-09 15:30:03 +08:00
parent d8192c3b07
commit c4ab7c158d
11 changed files with 185 additions and 40 deletions

View file

@ -1,15 +1,27 @@
package model
import "gorm.io/gorm"
import (
"time"
type OperateLog struct {
"gorm.io/gorm"
)
type OperationLog struct {
gorm.Model
Name string `gorm:"type:varchar(64)"`
Type string `gorm:"type:varchar(64)"`
User string `gorm:"type:varchar(64)"`
Path string `gorm:"type:varchar(64)"`
IP string `gorm:"type:varchar(64)"`
UserAgent string `gorm:"type:varchar(64)"`
Source string `gorm:"type:varchar(64)"`
Detail string `gorm:"type:longText"`
Group string `gorm:"type:varchar(64)" json:"type"`
Source string `gorm:"type:varchar(64)" json:"source"`
Action string `gorm:"type:varchar(64)" json:"action"`
IP string `gorm:"type:varchar(64)" json:"ip"`
Path string `gorm:"type:varchar(64)" json:"path"`
Method string `gorm:"type:varchar(64)" json:"method"`
UserAgent string `gorm:"type:varchar(64)" json:"userAgent"`
Body string `gorm:"type:text(65535)" json:"body"`
Resp string `gorm:"type:text(65535)" json:"resp"`
Status int `gorm:"type:varchar(64)" json:"status"`
Latency time.Duration `gorm:"type:varchar(64)" json:"latency"`
ErrorMessage string `gorm:"type:varchar(256)" json:"errorMessage"`
Detail string `gorm:"type:longText" json:"detail"`
}

View file

@ -2,6 +2,7 @@ package repo
type RepoGroup struct {
UserRepo
OperationRepo
CommonRepo
}

View file

@ -0,0 +1,20 @@
package repo
import (
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global"
)
type OperationRepo struct{}
type IOperationRepo interface {
Create(user *model.OperationLog) error
}
func NewIOperationService() IOperationRepo {
return &OperationRepo{}
}
func (u *OperationRepo) Create(user *model.OperationLog) error {
return global.DB.Create(user).Error
}

View file

@ -9,6 +9,7 @@ type ServiceGroup struct {
var ServiceGroupApp = new(ServiceGroup)
var (
userRepo = repo.RepoGroupApp.UserRepo
commonRepo = repo.RepoGroupApp.CommonRepo
userRepo = repo.RepoGroupApp.UserRepo
operationRepo = repo.RepoGroupApp.OperationRepo
commonRepo = repo.RepoGroupApp.CommonRepo
)

View file

@ -0,0 +1,17 @@
package service
import "github.com/1Panel-dev/1Panel/app/model"
type OperationService struct{}
type IOperationService interface {
Create(operation model.OperationLog) error
}
func NewIOperationService() IOperationService {
return &OperationService{}
}
func (u *OperationService) Create(operation model.OperationLog) error {
return operationRepo.Create(&operation)
}

View file

@ -11,6 +11,7 @@ func Init() {
m := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{
migrations.InitTable,
migrations.AddData,
migrations.AddTableOperationLog,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View file

@ -24,3 +24,10 @@ var AddData = &gormigrate.Migration{
return tx.Create(&user).Error
},
}
var AddTableOperationLog = &gormigrate.Migration{
ID: "20200809-add-table-operation-log",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.OperationLog{})
},
}

View file

@ -1,23 +0,0 @@
package middleware
import (
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global"
"github.com/gin-gonic/gin"
)
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
operateLog := model.OperateLog{
Path: path,
IP: c.ClientIP(),
UserAgent: c.Request.UserAgent(),
}
global.DB.Model(model.OperateLog{}).Save(&operateLog)
c.Next()
}
}
//TODO 根据URL写操作日志

View file

@ -0,0 +1,107 @@
package middleware
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/app/service"
"github.com/1Panel-dev/1Panel/global"
"github.com/gin-gonic/gin"
)
func OperationRecord() gin.HandlerFunc {
return func(c *gin.Context) {
var body []byte
if c.Request.Method != http.MethodGet {
var err error
body, err = ioutil.ReadAll(c.Request.Body)
if err != nil {
global.LOG.Errorf("read body from request failed, err: %v", err)
} else {
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
} else {
query := c.Request.URL.RawQuery
query, _ = url.QueryUnescape(query)
split := strings.Split(query, "&")
m := make(map[string]string)
for _, v := range split {
kv := strings.Split(v, "=")
if len(kv) == 2 {
m[kv[0]] = kv[1]
}
}
body, _ = json.Marshal(&m)
}
pathInfo := loadLogInfo(c.Request.URL.Path)
record := model.OperationLog{
Group: pathInfo.group,
Source: pathInfo.source,
Action: pathInfo.action,
IP: c.ClientIP(),
Method: c.Request.Method,
Path: c.Request.URL.Path,
UserAgent: c.Request.UserAgent(),
Body: string(body),
}
writer := responseBodyWriter{
ResponseWriter: c.Writer,
body: &bytes.Buffer{},
}
c.Writer = writer
now := time.Now()
c.Next()
latency := time.Since(now)
record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
record.Status = c.Writer.Status()
record.Latency = latency
record.Resp = writer.body.String()
if err := service.NewIOperationService().Create(record); err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
}
}
}
type responseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (r responseBodyWriter) Write(b []byte) (int, error) {
r.body.Write(b)
return r.ResponseWriter.Write(b)
}
type pathInfo struct {
group string
source string
action string
}
func loadLogInfo(path string) pathInfo {
if !strings.Contains(path, "/") {
return pathInfo{}
}
pathArrys := strings.Split(path, "/")
if len(pathArrys) < 2 {
return pathInfo{}
}
if len(pathArrys) == 2 {
return pathInfo{group: pathArrys[1]}
}
if len(pathArrys) == 3 {
return pathInfo{group: pathArrys[1], source: pathArrys[2]}
}
return pathInfo{group: pathArrys[1], source: pathArrys[2], action: pathArrys[3]}
}

View file

@ -2,18 +2,19 @@ package router
import (
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
"github.com/1Panel-dev/1Panel/middleware"
"github.com/gin-gonic/gin"
)
type BaseRouter struct{}
func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
baseRouter := Router.Group("base")
baseRouter := Router.Group("auth")
withRecordRouter := baseRouter.Use(middleware.OperationRecord())
baseApi := v1.ApiGroupApp.BaseApi
{
baseRouter.POST("login", baseApi.Login)
withRecordRouter.POST("login", baseApi.Login)
baseRouter.GET("captcha", baseApi.Captcha)
}
return baseRouter
}

View file

@ -12,10 +12,11 @@ type UserRouter struct{}
func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
userRouter := Router.Group("users")
userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
withRecordRouter := userRouter.Use(middleware.OperationRecord())
baseApi := v1.ApiGroupApp.BaseApi
{
userRouter.POST("", baseApi.Register)
userRouter.DELETE("", baseApi.DeleteUser)
withRecordRouter.POST("", baseApi.Register)
withRecordRouter.DELETE("", baseApi.DeleteUser)
userRouter.GET("", baseApi.GetUserList)
userRouter.GET(":name", baseApi.GetUserInfo)
}