// veza-backend-api/internal/api/user/handler.go package user import ( "net/http" "strconv" "time" "veza-backend-api/internal/common" "veza-backend-api/internal/response" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" "github.com/google/uuid" // Added import ) type Handler struct { service *Service dataExportService *services.DataExportService // BE-SVC-022: Service d'export de données } func NewHandler(service *Service, dataExportService *services.DataExportService) *Handler { return &Handler{ service: service, dataExportService: dataExportService, } } // GetMe récupère le profil de l'utilisateur connecté func (h *Handler) GetMe(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } user, err := h.service.GetUserByID(userID) if err != nil { response.NotFound(c, "User not found") return } response.Success(c, user) } // UpdateMe met à jour le profil de l'utilisateur connecté func (h *Handler) UpdateMe(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } var req UpdateUserRequest if !common.BindAndValidate(c, &req) { return } user, err := h.service.UpdateUser(userID, req) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, user) } // ChangePassword change le mot de passe de l'utilisateur func (h *Handler) ChangePassword(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } var req struct { CurrentPassword string `json:"current_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=8"` } if !common.BindAndValidate(c, &req) { return } err := h.service.ChangePassword(userID, req.CurrentPassword, req.NewPassword) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, nil) } // GetUsers liste tous les utilisateurs func (h *Handler) GetUsers(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) search := c.Query("search") users, total, err := h.service.GetUsers(page, limit, search) if err != nil { response.InternalServerError(c, "Failed to retrieve users") return } response.Success(c, gin.H{ "data": users, "pagination": gin.H{ "page": page, "limit": limit, "total": total, "total_pages": (total + limit - 1) / limit, }, }) } // GetUsersExceptMe liste tous les utilisateurs sauf l'utilisateur connecté func (h *Handler) GetUsersExceptMe(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) search := c.Query("search") // Ajouter le filtre pour exclure l'utilisateur actuel users, total, err := h.service.GetUsers(page, limit, search) if err != nil { response.InternalServerError(c, "Failed to retrieve users") return } // Filtrer l'utilisateur connecté filteredUsers := []UserResponse{} for _, user := range users { if user.ID != userID { // Direct comparison of uuid.UUID filteredUsers = append(filteredUsers, user) } } response.Success(c, gin.H{ "data": filteredUsers, "pagination": gin.H{ "page": page, "limit": limit, "total": total - 1, // -1 car on exclut l'utilisateur connecté "total_pages": (total + limit - 2) / limit, }, }) } // SearchUsers recherche des utilisateurs func (h *Handler) SearchUsers(c *gin.Context) { query := c.Query("q") if query == "" { response.BadRequest(c, "Query parameter 'q' is required") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) users, total, err := h.service.GetUsers(page, limit, query) if err != nil { response.InternalServerError(c, "Failed to search users") return } response.Success(c, gin.H{ "data": users, "pagination": gin.H{ "page": page, "limit": limit, "total": total, "total_pages": (total + limit - 1) / limit, }, }) } func (h *Handler) GetUserAvatar(c *gin.Context) { idStr := c.Param("id") userID, err := uuid.Parse(idStr) if err != nil { response.BadRequest(c, "Invalid user ID") return } user, err := h.service.GetUserByID(userID) if err != nil { response.NotFound(c, "User not found") return } // ✅ Correct way to handle sql.NullString if !user.Avatar.Valid || user.Avatar.String == "" { response.NotFound(c, "No avatar found") return } // Rediriger vers l'URL de l'avatar ou servir le fichier c.Redirect(http.StatusFound, user.Avatar.String) } // GetPreferences récupère les préférences de l'utilisateur connecté func (h *Handler) GetPreferences(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } preferences, err := h.service.GetUserPreferences(userID) if err != nil { response.InternalServerError(c, "Failed to get preferences") return } response.Success(c, preferences) } // UpdatePreferences met à jour les préférences de l'utilisateur connecté func (h *Handler) UpdatePreferences(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } var req UserPreferencesRequest if !common.BindAndValidate(c, &req) { return } preferences, err := h.service.UpdateUserPreferences(userID, req) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, preferences) } // DeleteAccount supprime le compte de l'utilisateur (soft delete) func (h *Handler) DeleteAccount(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } var req struct { Password string `json:"password" binding:"required"` Reason string `json:"reason"` ConfirmText string `json:"confirm_text" binding:"required"` } if !common.BindAndValidate(c, &req) { return } // Vérifier le texte de confirmation if req.ConfirmText != "DELETE" { response.BadRequest(c, "Confirmation text must be 'DELETE'") return } err := h.service.DeleteAccount(userID, req.Password, req.Reason) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, nil) } // RecoverAccount récupère un compte supprimé func (h *Handler) RecoverAccount(c *gin.Context) { var req struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` } if !common.BindAndValidate(c, &req) { return } err := h.service.RecoverAccount(req.Email, req.Password) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, nil) } // ExportData exporte les données de l'utilisateur (RGPD) - BE-SVC-022 func (h *Handler) ExportData(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } // Utiliser le nouveau service d'export de données if h.dataExportService == nil { response.InternalServerError(c, "Data export service not available") return } // Exporter les données au format JSON jsonData, err := h.dataExportService.ExportUserDataAsJSON(c.Request.Context(), userID) if err != nil { response.InternalServerError(c, "Failed to export user data: "+err.Error()) return } // Définir les headers pour le téléchargement filename := "veza-data-export-" + time.Now().Format("2006-01-02T15-04-05") + ".json" c.Header("Content-Type", "application/json") c.Header("Content-Disposition", `attachment; filename="`+filename+`"`) c.Header("Content-Length", strconv.Itoa(len(jsonData))) // Envoyer le fichier JSON c.Data(http.StatusOK, "application/json", jsonData) } // RequestDataDeletion demande la suppression définitive des données (RGPD) func (h *Handler) RequestDataDeletion(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } var req struct { Password string `json:"password" binding:"required"` Reason string `json:"reason"` } if !common.BindAndValidate(c, &req) { return } err := h.service.RequestDataDeletion(userID, req.Password, req.Reason) if err != nil { response.BadRequest(c, err.Error()) return } response.Success(c, nil) } // GetAccountStatus récupère le statut du compte func (h *Handler) GetAccountStatus(c *gin.Context) { userID, exists := common.GetUserIDFromContext(c) if !exists { response.Unauthorized(c, "User ID not found") return } status, err := h.service.GetAccountStatus(userID) if err != nil { response.InternalServerError(c, "Failed to get account status") return } response.Success(c, status) }