veza/veza-backend-api/internal/handlers/avatar_handler.go
senke 48734f8526 [BE-API-022] be-api: Implement avatar delete endpoint
- DeleteAvatar handler was already implemented and standardized
- Added route: DELETE /users/:userId/avatar
- Handler validates user authentication and ownership
- Handler deletes avatar file from storage and updates database
- Handler uses standard API response format

Phase: PHASE-2
Priority: P1
Progress: 31/267 (11.6%)
2025-12-24 11:36:15 +01:00

139 lines
4.2 KiB
Go

package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
// AvatarHandler handles avatar-related operations
type AvatarHandler struct {
imageService *services.ImageService
userService *services.UserService
}
// NewAvatarHandler creates a new AvatarHandler instance
func NewAvatarHandler(imageService *services.ImageService, userService *services.UserService) *AvatarHandler {
return &AvatarHandler{
imageService: imageService,
userService: userService,
}
}
// UploadAvatar handles avatar upload
// POST /api/v1/users/:userId/avatar
// BE-API-021: Implement avatar upload endpoint
// T0221: Validates user_id, file format/size, processes image, uploads to S3, and updates DB
func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
// Récupérer l'ID utilisateur depuis l'URL (peut être "id" ou "userId")
userIDStr := c.Param("userId")
if userIDStr == "" {
userIDStr = c.Param("id")
}
userID, err := uuid.Parse(userIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
return
}
// Vérifier que l'utilisateur est authentifié
authenticatedUserID, ok := GetUserIDUUID(c)
if !ok {
return // Erreur déjà envoyée par GetUserIDUUID
}
// Vérifier que l'utilisateur ne peut modifier que son propre avatar
if userID != authenticatedUserID {
RespondWithAppError(c, apperrors.NewForbiddenError("cannot update other user's avatar"))
return
}
// Récupérer le fichier
fileHeader, err := c.FormFile("avatar")
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("no file provided"))
return
}
// Valider et traiter l'image
resizedImage, err := h.imageService.ProcessAvatar(fileHeader)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
return
}
// Générer la clé S3
s3Key := h.imageService.GenerateS3Key(userID)
// Upload vers S3 (ou stockage local pour l'instant)
avatarURL, err := h.imageService.UploadToS3(resizedImage, s3Key)
if err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to upload avatar", err))
return
}
// Mettre à jour l'URL de l'avatar dans la DB
if err := h.userService.UpdateAvatarURL(userID, avatarURL); err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to update avatar", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"avatar_url": avatarURL})
}
// DeleteAvatar handles avatar deletion
// DELETE /api/v1/users/:userId/avatar
// BE-API-022: Implement avatar delete endpoint
// T0222: Validates user_id, deletes file from S3, and sets avatar_url to NULL in DB
func (h *AvatarHandler) DeleteAvatar(c *gin.Context) {
// Récupérer l'ID utilisateur depuis l'URL (peut être "id" ou "userId")
userIDStr := c.Param("userId")
if userIDStr == "" {
userIDStr = c.Param("id")
}
userID, err := uuid.Parse(userIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
return
}
// Vérifier que l'utilisateur est authentifié
authenticatedUserID, ok := GetUserIDUUID(c)
if !ok {
return // Erreur déjà envoyée par GetUserIDUUID
}
// Vérifier que l'utilisateur ne peut supprimer que son propre avatar
if userID != authenticatedUserID {
RespondWithAppError(c, apperrors.NewForbiddenError("cannot delete other user's avatar"))
return
}
// Récupérer l'utilisateur actuel pour obtenir l'URL de l'avatar
user, err := h.userService.GetByID(userID)
if err != nil {
RespondWithAppError(c, apperrors.NewNotFoundError("user"))
return
}
// Supprimer le fichier de S3 (ou stockage local) s'il existe
if user.Avatar != "" {
if err := h.imageService.DeleteFromS3(user.Avatar); err != nil {
// Logger l'erreur mais continuer (le fichier peut déjà être supprimé)
// En production, vous pourriez vouloir utiliser un logger ici
_ = err
}
}
// Mettre l'URL de l'avatar à une chaîne vide (NULL dans la DB)
if err := h.userService.UpdateAvatarURL(userID, ""); err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to delete avatar", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "avatar deleted"})
}