155 lines
3.9 KiB
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
|
|
} |