[BE-API-041] be-api: Implement user delete endpoint with soft delete support

This commit is contained in:
senke 2025-12-24 15:03:21 +01:00
parent 0657b79d09
commit 5a41b8c976
4 changed files with 91 additions and 2 deletions

View file

@ -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",

View file

@ -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)

View file

@ -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"})
}

View file

@ -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
}