NAM-APJATEL-BACKEND/middleware/activity_logs_middleware.go

155 lines
3.9 KiB
Go

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
}