package middleware import ( "bytes" "encoding/json" "fmt" "io" "strings" "users_management/m/config" "users_management/m/usecase" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type responseWriter struct { gin.ResponseWriter body *bytes.Buffer } func (r responseWriter) Write(b []byte) (int, error) { r.body.Write(b) return r.ResponseWriter.Write(b) } // ConditionalActivityLoggingMiddleware that respects auth config func ConditionalActivityLoggingMiddleware(activityLogUC usecase.ActivityLogUseCase, cfg *config.Config) gin.HandlerFunc { return func(c *gin.Context) { // If auth is disabled, skip activity logging if !cfg.AuthConfig.UserAuthEnabled { c.Next() return } // If auth is enabled, run normal activity logging ActivityLoggingMiddleware(activityLogUC)(c) } } func ActivityLoggingMiddleware(activityLogUC usecase.ActivityLogUseCase) gin.HandlerFunc { return func(c *gin.Context) { // Skip logging for certain endpoints if shouldSkipLogging(c.Request.URL.Path) { c.Next() return } // Get user info from context (set by auth middleware) userID, userExists := c.Get("userID") if !userExists { c.Next() return } // Convert userID to UUID uid, ok := userID.(uuid.UUID) if !ok { c.Next() return } // Read request body var requestBody []byte if c.Request.Body != nil { requestBody, _ = io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) } // Wrap response writer to capture response w := &responseWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer} c.Writer = w // Process request c.Next() // Log the activity in background go func() { action := getActionFromMethod(c.Request.Method) resource := getResourceFromPath(c.Request.URL.Path) resourceID := getResourceIDFromPath(c.Request.URL.Path) var oldData, newData interface{} // For updates, you might want to fetch old data before the operation // This is a simplified version if len(requestBody) > 0 { json.Unmarshal(requestBody, &newData) } details := "" if c.Writer.Status() >= 400 { details = fmt.Sprintf("Request failed with status: %d", c.Writer.Status()) } activityLogUC.LogActivity( uid, action, resource, resourceID, oldData, newData, c.ClientIP(), c.Request.UserAgent(), details, ) }() } } func shouldSkipLogging(path string) bool { skipPaths := []string{ "/api/v1/logs", "/api/v1/users/login", "/api/v1/users/logout", "/uploads", } for _, skipPath := range skipPaths { if strings.HasPrefix(path, skipPath) { return true } } return false } func getActionFromMethod(method string) string { switch method { case "POST": return "CREATE" case "PUT", "PATCH": return "UPDATE" case "DELETE": return "DELETE" case "GET": return "READ" default: return method } } func getResourceFromPath(path string) string { parts := strings.Split(path, "/") if len(parts) >= 4 { return parts[3] // /api/v1/devices -> devices } return "unknown" } func getResourceIDFromPath(path string) *string { parts := strings.Split(path, "/") if len(parts) >= 5 { id := parts[4] return &id } return nil }