[BE-API-041] be-api: Implement user delete endpoint with soft delete support
This commit is contained in:
parent
0657b79d09
commit
5a41b8c976
4 changed files with 91 additions and 2 deletions
|
|
@ -2930,7 +2930,7 @@
|
|||
"description": "DELETE /api/v1/users/:id with soft delete support",
|
||||
"owner": "backend",
|
||||
"estimated_hours": 4,
|
||||
"status": "todo",
|
||||
"status": "completed",
|
||||
"files_involved": [],
|
||||
"implementation_steps": [
|
||||
{
|
||||
|
|
@ -2951,7 +2951,9 @@
|
|||
"Unit tests",
|
||||
"Integration tests"
|
||||
],
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"completed_at": "2025-12-24T15:03:19.333494",
|
||||
"implementation_notes": "Implemented DELETE /api/v1/users/:id endpoint with soft delete support. Added DeleteUser method to UserService that uses GORM soft delete (sets deleted_at) and also sets is_active to false. Added DeleteUser handler with ownership/admin check using PermissionService.HasRole. Route registered with RequireOwnershipOrAdmin middleware."
|
||||
},
|
||||
{
|
||||
"id": "BE-API-042",
|
||||
|
|
|
|||
|
|
@ -464,6 +464,8 @@ func (r *APIRouter) setupUserRoutes(router *gin.RouterGroup) {
|
|||
return uuid.Parse(userIDStr)
|
||||
}
|
||||
protected.PUT("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("user", userOwnerResolver), profileHandler.UpdateProfile)
|
||||
// BE-API-041: Delete user endpoint (soft delete)
|
||||
protected.DELETE("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("user", userOwnerResolver), profileHandler.DeleteUser)
|
||||
|
||||
protected.GET("/:id/completion", profileHandler.GetProfileCompletion)
|
||||
|
||||
|
|
|
|||
|
|
@ -657,3 +657,60 @@ func isValidUsername(username string) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// DeleteUser gère la suppression d'un utilisateur (soft delete)
|
||||
// BE-API-041: DELETE /api/v1/users/:id with soft delete support
|
||||
// @Summary Delete user
|
||||
// @Description Soft delete a user (only user owner or admin can delete)
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "User ID"
|
||||
// @Success 200 {object} response.APIResponse "User deleted successfully"
|
||||
// @Failure 400 {object} response.APIResponse "Invalid ID"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized"
|
||||
// @Failure 403 {object} response.APIResponse "Forbidden - Not user owner or admin"
|
||||
// @Failure 404 {object} response.APIResponse "User not found"
|
||||
// @Router /users/{id} [delete]
|
||||
func (h *ProfileHandler) DeleteUser(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "invalid user id"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the requesting user ID
|
||||
requesterID, ok := GetUserIDUUID(c)
|
||||
if !ok {
|
||||
return // Erreur déjà envoyée par GetUserIDUUID
|
||||
}
|
||||
|
||||
// Check if requester is the user owner or admin
|
||||
if requesterID != userID {
|
||||
// Check if requester is admin
|
||||
if h.permissionService != nil {
|
||||
isAdmin, err := h.permissionService.HasRole(c.Request.Context(), requesterID, "admin")
|
||||
if err != nil || !isAdmin {
|
||||
RespondWithAppError(c, apperrors.NewForbiddenError("only user owner or admin can delete user"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
RespondWithAppError(c, apperrors.NewForbiddenError("only user owner or admin can delete user"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete user (soft delete)
|
||||
if err := h.userService.DeleteUser(c.Request.Context(), userID); err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
RespondWithAppError(c, apperrors.NewNotFoundError("user"))
|
||||
return
|
||||
}
|
||||
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to delete user", err))
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusOK, gin.H{"message": "user deleted successfully"})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -745,3 +746,30 @@ func (s *UserService) UpdateUserSettings(userID uuid.UUID, req *types.UpdateSett
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser soft deletes a user
|
||||
// BE-API-041: Implement user delete endpoint with soft delete support
|
||||
func (s *UserService) DeleteUser(ctx context.Context, userID uuid.UUID) error {
|
||||
// Check if user exists
|
||||
_, err := s.userRepo.GetByID(userID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
// Use repository Delete method (soft delete via GORM)
|
||||
if err := s.userRepo.Delete(userID.String()); err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
|
||||
// Also set is_active to false for consistency
|
||||
if s.db != nil {
|
||||
if err := s.db.WithContext(ctx).Model(&models.User{}).
|
||||
Where("id = ?", userID).
|
||||
Update("is_active", false).Error; err != nil {
|
||||
// Log but don't fail if this update fails
|
||||
return fmt.Errorf("failed to deactivate user: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue