feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
This commit is contained in:
125
internal/api/middleware/operation_log.go
Normal file
125
internal/api/middleware/operation_log.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/user-management-system/internal/domain"
|
||||
"github.com/user-management-system/internal/repository"
|
||||
)
|
||||
|
||||
type OperationLogMiddleware struct {
|
||||
repo *repository.OperationLogRepository
|
||||
}
|
||||
|
||||
func NewOperationLogMiddleware(repo *repository.OperationLogRepository) *OperationLogMiddleware {
|
||||
return &OperationLogMiddleware{repo: repo}
|
||||
}
|
||||
|
||||
type bodyWriter struct {
|
||||
gin.ResponseWriter
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func newBodyWriter(w gin.ResponseWriter) *bodyWriter {
|
||||
return &bodyWriter{ResponseWriter: w, statusCode: 200}
|
||||
}
|
||||
|
||||
func (bw *bodyWriter) WriteHeader(code int) {
|
||||
bw.statusCode = code
|
||||
bw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (bw *bodyWriter) WriteHeaderNow() {
|
||||
bw.ResponseWriter.WriteHeaderNow()
|
||||
}
|
||||
|
||||
func (m *OperationLogMiddleware) Record() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
if method == "GET" || method == "HEAD" || method == "OPTIONS" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
var reqParams string
|
||||
if c.Request.Body != nil {
|
||||
bodyBytes, err := io.ReadAll(io.LimitReader(c.Request.Body, 4096))
|
||||
if err == nil {
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
reqParams = sanitizeParams(bodyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
bw := newBodyWriter(c.Writer)
|
||||
c.Writer = bw
|
||||
|
||||
c.Next()
|
||||
|
||||
var userIDPtr *int64
|
||||
if uid, exists := c.Get("user_id"); exists {
|
||||
if id, ok := uid.(int64); ok {
|
||||
userID := id
|
||||
userIDPtr = &userID
|
||||
}
|
||||
}
|
||||
|
||||
logEntry := &domain.OperationLog{
|
||||
UserID: userIDPtr,
|
||||
OperationType: methodToType(method),
|
||||
OperationName: c.FullPath(),
|
||||
RequestMethod: method,
|
||||
RequestPath: c.Request.URL.Path,
|
||||
RequestParams: reqParams,
|
||||
ResponseStatus: bw.statusCode,
|
||||
IP: c.ClientIP(),
|
||||
UserAgent: c.Request.UserAgent(),
|
||||
}
|
||||
|
||||
go func(entry *domain.OperationLog) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
_ = m.repo.Create(ctx, entry)
|
||||
}(logEntry)
|
||||
}
|
||||
}
|
||||
|
||||
func methodToType(method string) string {
|
||||
switch method {
|
||||
case "POST":
|
||||
return "CREATE"
|
||||
case "PUT", "PATCH":
|
||||
return "UPDATE"
|
||||
case "DELETE":
|
||||
return "DELETE"
|
||||
default:
|
||||
return "OTHER"
|
||||
}
|
||||
}
|
||||
|
||||
func sanitizeParams(data []byte) string {
|
||||
var payload map[string]interface{}
|
||||
if err := json.Unmarshal(data, &payload); err != nil {
|
||||
if len(data) > 500 {
|
||||
return string(data[:500]) + "..."
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
for _, field := range []string{"password", "old_password", "new_password", "confirm_password", "secret", "token"} {
|
||||
if _, ok := payload[field]; ok {
|
||||
payload[field] = "***"
|
||||
}
|
||||
}
|
||||
|
||||
result, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
Reference in New Issue
Block a user