102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"github.com/gin-gonic/gin"
|
|
"io"
|
|
"kra/internal/biz"
|
|
"kra/internal/service"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type captureWriter struct {
|
|
gin.ResponseWriter
|
|
body bytes.Buffer
|
|
}
|
|
|
|
func (w *captureWriter) Write(data []byte) (int, error) {
|
|
if w.body.Len() < 32768 {
|
|
remaining := 32768 - w.body.Len()
|
|
if len(data) > remaining {
|
|
_, _ = w.body.Write(data[:remaining])
|
|
} else {
|
|
_, _ = w.body.Write(data)
|
|
}
|
|
}
|
|
return w.ResponseWriter.Write(data)
|
|
}
|
|
func redactJSON(raw []byte) string {
|
|
if len(raw) == 0 {
|
|
return ""
|
|
}
|
|
if len(raw) > 32768 {
|
|
raw = raw[:32768]
|
|
}
|
|
var value any
|
|
if json.Unmarshal(raw, &value) != nil {
|
|
return string(raw)
|
|
}
|
|
var clean func(any)
|
|
clean = func(current any) {
|
|
switch v := current.(type) {
|
|
case map[string]any:
|
|
for key, item := range v {
|
|
lower := strings.ToLower(key)
|
|
if strings.Contains(lower, "password") || strings.Contains(lower, "token") || strings.Contains(lower, "secret") {
|
|
v[key] = "******"
|
|
} else {
|
|
clean(item)
|
|
}
|
|
}
|
|
case []any:
|
|
for _, item := range v {
|
|
clean(item)
|
|
}
|
|
}
|
|
}
|
|
clean(value)
|
|
encoded, _ := json.Marshal(value)
|
|
return string(encoded)
|
|
}
|
|
func operationAudit(svc *service.AuditService) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if c.Request.Method == "GET" || c.Request.Method == "HEAD" || c.Request.Method == "OPTIONS" {
|
|
c.Next()
|
|
return
|
|
}
|
|
path := c.Request.URL.Path
|
|
if strings.Contains(path, "sysOperationRecord") || strings.Contains(path, "sysLoginLog") || strings.Contains(path, "dataAccessLog") {
|
|
c.Next()
|
|
return
|
|
}
|
|
var requestBody []byte
|
|
if c.Request.Body != nil {
|
|
requestBody, _ = io.ReadAll(io.LimitReader(c.Request.Body, 32769))
|
|
c.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
|
|
}
|
|
writer := &captureWriter{ResponseWriter: c.Writer}
|
|
c.Writer = writer
|
|
started := time.Now()
|
|
c.Next()
|
|
userID := uint(0)
|
|
if claims := currentClaims(c); claims != nil {
|
|
userID = claims.ID
|
|
}
|
|
requestID, _ := c.Get("request_id")
|
|
status := c.Writer.Status()
|
|
errorMessage := ""
|
|
if status >= 400 {
|
|
errorMessage = writer.body.String()
|
|
}
|
|
_ = svc.RecordOperation(c.Request.Context(), &biz.OperationRecord{IP: c.ClientIP(), Method: c.Request.Method, Path: path, Status: status, LatencyMS: time.Since(started).Milliseconds(), Agent: c.Request.UserAgent(), ErrorMessage: errorMessage, Body: redactJSON(requestBody), Response: redactJSON(writer.body.Bytes()), UserID: userID, RequestID: toString(requestID), TraceID: c.GetHeader("traceparent"), DeviceID: c.GetHeader("X-Device-Id")})
|
|
}
|
|
}
|
|
func toString(value any) string {
|
|
if text, ok := value.(string); ok {
|
|
return text
|
|
}
|
|
return ""
|
|
}
|