package audit import ( "bytes" "io" "strings" "github.com/gin-gonic/gin" ) type ActorResolver func(*gin.Context) (id string, name string) func Middleware(sourceService string, resolveActor ActorResolver) gin.HandlerFunc { sourceService = strings.TrimSpace(sourceService) return func(ctx *gin.Context) { if ctx.Request.Method == "GET" || ctx.Request.Method == "HEAD" || ctx.Request.Method == "OPTIONS" { ctx.Next() return } var body []byte if ctx.Request.Body != nil { body, _ = io.ReadAll(ctx.Request.Body) ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body)) } ctx.Next() actorID, actorName := "", "" if resolveActor != nil { actorID, actorName = resolveActor(ctx) } if actorID == "" { actorID = firstNonEmpty(ctx.GetHeader("X-User-Id"), ctx.GetHeader("X-Actor-Id"), "unknown") } if actorName == "" { actorName = firstNonEmpty(ctx.GetHeader("X-User-Name"), ctx.GetHeader("X-Actor-Name")) } result := "success" if len(ctx.Errors) > 0 || ctx.Writer.Status() >= 400 { result = "failed" } _, _ = SaveRecord(Record{ TraceID: firstNonEmpty(ctx.GetHeader("X-Trace-Id"), ctx.GetHeader("Request-Id")), SourceService: sourceService, ActorID: actorID, ActorName: actorName, Action: ctx.Request.Method + " " + ctx.FullPath(), ObjectType: routeObjectType(ctx.FullPath()), ObjectID: firstNonEmpty(ctx.Param("id"), ctx.Query("id"), ctx.FullPath()), RequestMethod: ctx.Request.Method, RequestPath: ctx.FullPath(), ClientIP: ctx.ClientIP(), AfterJSON: string(body), Result: result, }) } } func routeObjectType(path string) string { path = strings.Trim(path, "/") if path == "" { return "unknown" } parts := strings.Split(path, "/") return parts[len(parts)-1] }