- Updated system_metrics.go to use RespondSuccess() helper
- Updated bitrate_handler.go success responses to use wrapped format
- Updated frontend_log_handler.go to use RespondSuccess() helper
- Updated csrf.go to use RespondSuccess() and RespondWithError() helpers
- Updated audit.go: all 30+ error and success responses now use wrapped format helpers
- Updated comment_handler.go error responses to use RespondWithError()
- Updated system_metrics_test.go to expect wrapped format {success, data}
- All handlers now consistently use wrapped format helpers
- Build and tests pass successfully
- Action 1.3.2.1 complete - backend handlers standardized to wrapped format
588 lines
20 KiB
Go
588 lines
20 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
apperrors "veza-backend-api/internal/errors"
|
|
"veza-backend-api/internal/services"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// AuditServiceInterfaceForAuditHandler defines methods needed for audit handler
|
|
type AuditServiceInterfaceForAuditHandler interface {
|
|
SearchLogs(ctx context.Context, req *services.AuditLogSearchRequest) ([]*services.AuditLog, error)
|
|
CountLogs(ctx context.Context, req *services.AuditLogSearchRequest) (int64, error)
|
|
GetUserActivity(ctx context.Context, userID uuid.UUID, limit int) ([]*services.AuditLog, error)
|
|
GetIPActivity(ctx context.Context, ipAddress string, limit int) ([]*services.AuditLog, error)
|
|
GetStats(ctx context.Context, startDate, endDate time.Time) ([]*services.AuditStats, error)
|
|
DetectSuspiciousActivity(ctx context.Context, hours int) ([]*services.SuspiciousActivity, error)
|
|
CleanupOldLogs(ctx context.Context, retentionDays int) (int64, error)
|
|
}
|
|
|
|
// AuditHandler gère les opérations sur les logs d'audit
|
|
type AuditHandler struct {
|
|
auditService AuditServiceInterfaceForAuditHandler
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewAuditHandler crée un nouveau handler d'audit
|
|
func NewAuditHandler(
|
|
auditService *services.AuditService,
|
|
logger *zap.Logger,
|
|
) *AuditHandler {
|
|
return &AuditHandler{
|
|
auditService: &auditServiceWrapperForAuditHandler{auditService: auditService},
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// NewAuditHandlerWithInterface creates a new audit handler with interface (for testing)
|
|
func NewAuditHandlerWithInterface(
|
|
auditService AuditServiceInterfaceForAuditHandler,
|
|
logger *zap.Logger,
|
|
) *AuditHandler {
|
|
return &AuditHandler{
|
|
auditService: auditService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// auditServiceWrapperForAuditHandler wraps *services.AuditService to implement AuditServiceInterfaceForAuditHandler
|
|
type auditServiceWrapperForAuditHandler struct {
|
|
auditService *services.AuditService
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) SearchLogs(ctx context.Context, req *services.AuditLogSearchRequest) ([]*services.AuditLog, error) {
|
|
return w.auditService.SearchLogs(ctx, req)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) CountLogs(ctx context.Context, req *services.AuditLogSearchRequest) (int64, error) {
|
|
return w.auditService.CountLogs(ctx, req)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) GetUserActivity(ctx context.Context, userID uuid.UUID, limit int) ([]*services.AuditLog, error) {
|
|
return w.auditService.GetUserActivity(ctx, userID, limit)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) GetIPActivity(ctx context.Context, ipAddress string, limit int) ([]*services.AuditLog, error) {
|
|
return w.auditService.GetIPActivity(ctx, ipAddress, limit)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) GetStats(ctx context.Context, startDate, endDate time.Time) ([]*services.AuditStats, error) {
|
|
return w.auditService.GetStats(ctx, startDate, endDate)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) DetectSuspiciousActivity(ctx context.Context, hours int) ([]*services.SuspiciousActivity, error) {
|
|
return w.auditService.DetectSuspiciousActivity(ctx, hours)
|
|
}
|
|
|
|
func (w *auditServiceWrapperForAuditHandler) CleanupOldLogs(ctx context.Context, retentionDays int) (int64, error) {
|
|
return w.auditService.CleanupOldLogs(ctx, retentionDays)
|
|
}
|
|
|
|
// SearchLogs recherche des logs d'audit
|
|
// @Summary Search audit logs
|
|
// @Description Search and filter audit logs with pagination support. Supports filtering by action, resource, date range, IP address, and user agent.
|
|
// @Tags Audit
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param action query string false "Filter by action type"
|
|
// @Param resource query string false "Filter by resource type"
|
|
// @Param resource_id query string false "Filter by resource ID (UUID)"
|
|
// @Param ip_address query string false "Filter by IP address"
|
|
// @Param user_agent query string false "Filter by user agent"
|
|
// @Param start_date query string false "Start date filter (YYYY-MM-DD)"
|
|
// @Param end_date query string false "End date filter (YYYY-MM-DD)"
|
|
// @Param page query int false "Page number" default(1)
|
|
// @Param limit query int false "Items per page" default(20)
|
|
// @Param offset query int false "Offset for pagination"
|
|
// @Success 200 {object} handlers.APIResponse{data=object{logs=array,pagination=object}}
|
|
// @Failure 400 {object} handlers.APIResponse "Validation error"
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 500 {object} handlers.APIResponse "Internal server error"
|
|
// @Router /audit/logs [get]
|
|
// BE-API-034: Enhanced with better filtering and pagination
|
|
func (ah *AuditHandler) SearchLogs() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userID, ok := GetUserIDUUID(c)
|
|
if !ok {
|
|
return // Erreur déjà envoyée par GetUserIDUUID
|
|
}
|
|
|
|
// Parser les paramètres de recherche
|
|
req := &services.AuditLogSearchRequest{
|
|
UserID: &userID, // Par défaut, chercher les logs de l'utilisateur
|
|
}
|
|
|
|
// Paramètres optionnels de filtrage
|
|
if action := c.Query("action"); action != "" {
|
|
req.Action = action
|
|
}
|
|
if resource := c.Query("resource"); resource != "" {
|
|
req.Resource = resource
|
|
}
|
|
// BE-API-034: Added resource_id filter
|
|
if resourceIDStr := c.Query("resource_id"); resourceIDStr != "" {
|
|
if resourceID, err := uuid.Parse(resourceIDStr); err == nil {
|
|
req.ResourceID = &resourceID
|
|
}
|
|
}
|
|
// BE-API-034: Added IP address filter
|
|
if ipAddress := c.Query("ip_address"); ipAddress != "" {
|
|
req.IPAddress = ipAddress
|
|
}
|
|
// BE-API-034: Added user_agent filter
|
|
if userAgent := c.Query("user_agent"); userAgent != "" {
|
|
req.UserAgent = userAgent
|
|
}
|
|
if startDateStr := c.Query("start_date"); startDateStr != "" {
|
|
if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil {
|
|
req.StartDate = &startDate
|
|
} else {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Invalid start_date format. Use YYYY-MM-DD"))
|
|
return
|
|
}
|
|
}
|
|
if endDateStr := c.Query("end_date"); endDateStr != "" {
|
|
if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil {
|
|
req.EndDate = &endDate
|
|
} else {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Invalid end_date format. Use YYYY-MM-DD"))
|
|
return
|
|
}
|
|
}
|
|
|
|
// BE-API-034: Improved pagination - support both page/limit and offset/limit
|
|
if pageStr := c.Query("page"); pageStr != "" {
|
|
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
|
|
req.Page = page
|
|
} else {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Invalid page number. Must be a positive integer"))
|
|
return
|
|
}
|
|
}
|
|
if limitStr := c.Query("limit"); limitStr != "" {
|
|
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 && limit <= 100 {
|
|
req.Limit = limit
|
|
} else {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Invalid limit. Must be between 1 and 100"))
|
|
return
|
|
}
|
|
} else {
|
|
req.Limit = 50 // Limite par défaut
|
|
}
|
|
if offsetStr := c.Query("offset"); offsetStr != "" {
|
|
if offset, err := strconv.Atoi(offsetStr); err == nil && offset >= 0 {
|
|
req.Offset = offset
|
|
} else {
|
|
RespondWithAppError(c, apperrors.NewValidationError("Invalid offset. Must be a non-negative integer"))
|
|
return
|
|
}
|
|
}
|
|
|
|
// Effectuer la recherche
|
|
logs, err := ah.auditService.SearchLogs(c.Request.Context(), req)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to search audit logs",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
)
|
|
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to search audit logs", err))
|
|
return
|
|
}
|
|
|
|
// BE-API-034: Get total count for pagination
|
|
total, err := ah.auditService.CountLogs(c.Request.Context(), req)
|
|
if err != nil {
|
|
ah.logger.Warn("Failed to count audit logs, continuing without total",
|
|
zap.Error(err),
|
|
)
|
|
total = int64(len(logs)) // Fallback to current count
|
|
}
|
|
|
|
// INT-007: Standardize pagination format
|
|
var pagination PaginationData
|
|
if req.Page > 0 {
|
|
pagination = BuildPaginationData(req.Page, req.Limit, total)
|
|
} else {
|
|
// Pour offset-based, on calcule la page approximative
|
|
page := (req.Offset / req.Limit) + 1
|
|
pagination = BuildPaginationData(page, req.Limit, total)
|
|
}
|
|
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"logs": logs,
|
|
"count": len(logs),
|
|
"pagination": pagination,
|
|
})
|
|
}
|
|
}
|
|
|
|
// GetStats récupère les statistiques d'audit
|
|
// @Summary Get audit statistics
|
|
// @Description Get audit statistics for the current user, optionally filtered by date range
|
|
// @Tags Audit
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param start_date query string false "Start date (YYYY-MM-DD)"
|
|
// @Param end_date query string false "End date (YYYY-MM-DD)"
|
|
// @Success 200 {object} handlers.APIResponse{data=object{stats=object}}
|
|
// @Failure 400 {object} handlers.APIResponse "Validation error"
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 500 {object} handlers.APIResponse "Internal server error"
|
|
// @Router /audit/stats [get]
|
|
func (ah *AuditHandler) GetStats() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Parser les paramètres de date
|
|
var startDate, endDate time.Time
|
|
var err error
|
|
|
|
if startDateStr := c.Query("start_date"); startDateStr != "" {
|
|
startDate, err = time.Parse("2006-01-02", startDateStr)
|
|
if err != nil {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeValidation), "Invalid start_date format")
|
|
return
|
|
}
|
|
} else {
|
|
startDate = time.Now().AddDate(0, 0, -30) // 30 jours par défaut
|
|
}
|
|
|
|
if endDateStr := c.Query("end_date"); endDateStr != "" {
|
|
endDate, err = time.Parse("2006-01-02", endDateStr)
|
|
if err != nil {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeValidation), "Invalid end_date format")
|
|
return
|
|
}
|
|
} else {
|
|
endDate = time.Now()
|
|
}
|
|
|
|
// Récupérer les statistiques
|
|
stats, err := ah.auditService.GetStats(c.Request.Context(), startDate, endDate)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to get audit stats",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get audit stats")
|
|
return
|
|
}
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"user_id": userID,
|
|
"start_date": startDate,
|
|
"end_date": endDate,
|
|
"stats": stats,
|
|
})
|
|
}
|
|
}
|
|
|
|
// GetUserActivity récupère l'activité d'un utilisateur
|
|
// @Summary Get user activity
|
|
// @Description Get recent activity logs for the current user
|
|
// @Tags Audit
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param limit query int false "Number of activities to return" default(50)
|
|
// @Success 200 {object} handlers.APIResponse{data=object{activities=array}}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 500 {object} handlers.APIResponse "Internal server error"
|
|
// @Router /audit/activity [get]
|
|
func (ah *AuditHandler) GetUserActivity() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Parser le paramètre limit
|
|
limit := 50 // Limite par défaut
|
|
if limitStr := c.Query("limit"); limitStr != "" {
|
|
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
|
limit = parsedLimit
|
|
}
|
|
}
|
|
|
|
// Récupérer l'activité
|
|
activity, err := ah.auditService.GetUserActivity(c.Request.Context(), userID, limit)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to get user activity",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get user activity")
|
|
return
|
|
}
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"user_id": userID,
|
|
"activity": activity,
|
|
"count": len(activity),
|
|
})
|
|
}
|
|
}
|
|
|
|
// DetectSuspiciousActivity détecte les activités suspectes
|
|
func (ah *AuditHandler) DetectSuspiciousActivity() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Parser le paramètre hours
|
|
hours := 24 // 24 heures par défaut
|
|
if hoursStr := c.Query("hours"); hoursStr != "" {
|
|
if parsedHours, err := strconv.Atoi(hoursStr); err == nil && parsedHours > 0 && parsedHours <= 168 {
|
|
hours = parsedHours
|
|
}
|
|
}
|
|
|
|
// Détecter les activités suspectes
|
|
activities, err := ah.auditService.DetectSuspiciousActivity(c.Request.Context(), hours)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to detect suspicious activity",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to detect suspicious activity")
|
|
return
|
|
}
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"user_id": userID,
|
|
"hours": hours,
|
|
"activities": activities,
|
|
"count": len(activities),
|
|
})
|
|
}
|
|
}
|
|
|
|
// GetIPActivity récupère l'activité d'une IP
|
|
func (ah *AuditHandler) GetIPActivity() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Récupérer l'IP depuis les paramètres
|
|
ipAddress := c.Param("ip")
|
|
if ipAddress == "" {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeValidation), "IP address parameter required")
|
|
return
|
|
}
|
|
|
|
// Parser le paramètre limit
|
|
limit := 50 // Limite par défaut
|
|
if limitStr := c.Query("limit"); limitStr != "" {
|
|
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
|
limit = parsedLimit
|
|
}
|
|
}
|
|
|
|
// Récupérer l'activité de l'IP
|
|
activity, err := ah.auditService.GetIPActivity(c.Request.Context(), ipAddress, limit)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to get IP activity",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
zap.String("ip_address", ipAddress),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get IP activity")
|
|
return
|
|
}
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"user_id": userID,
|
|
"ip_address": ipAddress,
|
|
"activity": activity,
|
|
"count": len(activity),
|
|
})
|
|
}
|
|
}
|
|
|
|
// CleanupOldLogs nettoie les anciens logs d'audit
|
|
func (ah *AuditHandler) CleanupOldLogs() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Parser le paramètre retention_days
|
|
retentionDays := 90 // 90 jours par défaut
|
|
if retentionStr := c.Query("retention_days"); retentionStr != "" {
|
|
if parsedRetention, err := strconv.Atoi(retentionStr); err == nil && parsedRetention > 0 && parsedRetention <= 365 {
|
|
retentionDays = parsedRetention
|
|
}
|
|
}
|
|
|
|
// Nettoyer les anciens logs
|
|
deletedCount, err := ah.auditService.CleanupOldLogs(c.Request.Context(), retentionDays)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to cleanup old audit logs",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to cleanup old logs")
|
|
return
|
|
}
|
|
|
|
ah.logger.Info("Old audit logs cleaned up",
|
|
zap.String("user_id", userID.String()),
|
|
zap.Int64("deleted_count", deletedCount),
|
|
zap.Int("retention_days", retentionDays),
|
|
)
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"message": "Old audit logs cleaned up successfully",
|
|
"deleted_count": deletedCount,
|
|
"retention_days": retentionDays,
|
|
})
|
|
}
|
|
}
|
|
|
|
// GetAuditLog récupère un log d'audit spécifique
|
|
func (ah *AuditHandler) GetAuditLog() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Récupérer l'ID utilisateur depuis le contexte
|
|
userIDInterface, exists := c.Get("user_id")
|
|
if !exists {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeUnauthorized), "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userID, ok := userIDInterface.(uuid.UUID)
|
|
if !ok {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Invalid user ID type")
|
|
return
|
|
}
|
|
|
|
// Récupérer l'ID du log depuis les paramètres
|
|
logIDStr := c.Param("id")
|
|
logID, err := uuid.Parse(logIDStr)
|
|
if err != nil {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeValidation), "Invalid log ID")
|
|
return
|
|
}
|
|
|
|
// Rechercher le log spécifique
|
|
req := &services.AuditLogSearchRequest{
|
|
UserID: &userID,
|
|
Limit: 1,
|
|
}
|
|
|
|
logs, err := ah.auditService.SearchLogs(c.Request.Context(), req)
|
|
if err != nil {
|
|
ah.logger.Error("Failed to get audit log",
|
|
zap.Error(err),
|
|
zap.String("user_id", userID.String()),
|
|
zap.String("log_id", logID.String()),
|
|
)
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeInternal), "Failed to get audit log")
|
|
return
|
|
}
|
|
|
|
if len(logs) == 0 {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeNotFound), "Audit log not found")
|
|
return
|
|
}
|
|
|
|
// Vérifier que le log appartient à l'utilisateur
|
|
log := logs[0]
|
|
if log.UserID != nil && *log.UserID != userID {
|
|
// Action 1.3.2.1: Use wrapped format helper for errors
|
|
RespondWithError(c, int(apperrors.ErrCodeForbidden), "Access denied")
|
|
return
|
|
}
|
|
|
|
// Action 1.3.2.1: Use wrapped format helper
|
|
RespondSuccess(c, http.StatusOK, gin.H{
|
|
"log": log,
|
|
})
|
|
}
|
|
}
|