feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers

This commit is contained in:
2026-04-02 11:19:50 +08:00
parent e59a77bc49
commit dcc1f186f8
298 changed files with 62603 additions and 0 deletions

View 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)
}