You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1Panel/backend/middleware/operation.go

169 lines
4.3 KiB

package middleware
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/cmd/server/operation"
"github.com/gin-gonic/gin"
)
func OperationLog() gin.HandlerFunc {
return func(c *gin.Context) {
if strings.Contains(c.Request.URL.Path, "search") || c.Request.Method == http.MethodGet {
c.Next()
return
}
group := loadLogInfo(c.Request.URL.Path)
record := model.OperationLog{
Group: group,
IP: c.ClientIP(),
Method: c.Request.Method,
Path: c.Request.URL.Path,
UserAgent: c.Request.UserAgent(),
}
var (
operationDics []operationJson
operationDic operationJson
)
if err := json.Unmarshal(operation.OperationJosn, &operationDics); err != nil {
c.Next()
return
}
for _, dic := range operationDics {
if dic.API == record.Path && dic.Method == record.Method {
operationDic = dic
break
}
}
if len(operationDic.API) == 0 {
c.Next()
return
}
formatMap := make(map[string]interface{})
if len(operationDic.BodyKeys) != 0 {
body, err := ioutil.ReadAll(c.Request.Body)
if err == nil {
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
bodyMap := make(map[string]interface{})
_ = json.Unmarshal(body, &bodyMap)
for _, key := range operationDic.BodyKeys {
if _, ok := bodyMap[key]; ok {
formatMap[key] = bodyMap[key]
}
}
}
if len(operationDic.BeforeFuntions) != 0 {
for _, funcs := range operationDic.BeforeFuntions {
for key, value := range formatMap {
if funcs.Info == key {
var names []string
if funcs.IsList {
if key == "ids" {
sql := fmt.Sprintf("SELECT %s FROM %s where id in (?);", funcs.Key, funcs.DB)
fmt.Println(value)
_ = global.DB.Raw(sql, value).Scan(&names)
}
} else {
_ = global.DB.Raw(fmt.Sprintf("select %s from %s where %s = ?;", funcs.Key, funcs.DB, key), value).Scan(&names)
}
formatMap[funcs.Value] = strings.Join(names, ",")
break
}
}
}
}
var values []interface{}
for key, value := range formatMap {
if strings.Contains(operationDic.FormatEN, key) {
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, key, "%v")
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, key, "%v")
values = append(values, value)
}
}
record.DetailZH = fmt.Sprintf(operationDic.FormatZH, values...)
record.DetailEN = fmt.Sprintf(operationDic.FormatEN, values...)
writer := responseBodyWriter{
ResponseWriter: c.Writer,
body: &bytes.Buffer{},
}
c.Writer = writer
now := time.Now()
c.Next()
var res response
_ = json.Unmarshal(writer.body.Bytes(), &res)
if res.Code == 200 {
record.Status = constant.StatusSuccess
} else {
record.Status = constant.StatusFailed
record.Message = res.Message
}
latency := time.Since(now)
record.Latency = latency
if err := service.NewILogService().CreateOperationLog(record); err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
}
}
}
type operationJson struct {
API string `json:"api"`
Method string `json:"method"`
BodyKeys []string `json:"bodyKeys"`
ParamKeys []string `json:"paramKeys"`
BeforeFuntions []functionInfo `json:"beforeFuntions"`
FormatZH string `json:"formatZH"`
FormatEN string `json:"formatEN"`
}
type functionInfo struct {
Info string `json:"info"`
IsList bool `json:"isList"`
DB string `json:"db"`
Key string `json:"key"`
Value string `json:"value"`
}
type response struct {
Code int `json:"code"`
Message string `json:"message"`
}
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)
}
func loadLogInfo(path string) string {
path = strings.ReplaceAll(path, "/api/v1", "")
if !strings.Contains(path, "/") {
return ""
}
pathArrys := strings.Split(path, "/")
if len(pathArrys) < 2 {
return ""
}
return pathArrys[1]
}