mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-09-05 06:04:35 +08:00
feat: 增加系统操作记录中间件
This commit is contained in:
parent
d8192c3b07
commit
c4ab7c158d
11 changed files with 185 additions and 40 deletions
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package repo
|
|||
|
||||
type RepoGroup struct {
|
||||
UserRepo
|
||||
OperationRepo
|
||||
CommonRepo
|
||||
}
|
||||
|
||||
|
|
20
backend/app/repo/operation_log.go
Normal file
20
backend/app/repo/operation_log.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
17
backend/app/service/operation_log.go
Normal file
17
backend/app/service/operation_log.go
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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{})
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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写操作日志
|
107
backend/middleware/operation.go
Normal file
107
backend/middleware/operation.go
Normal 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]}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue