package handlers import ( "github.com/gin-gonic/gin" "github.com/google/uuid" "net/http" "veza-backend-api/internal/common" "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 // T0221: Validates user_id, file format/size, processes image, uploads to S3, and updates DB func (h *AvatarHandler) UploadAvatar(c *gin.Context) { userIDStr := c.Param("id") userID, err := uuid.Parse(userIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } // Check that user_id corresponds to authenticated user authenticatedUserID, exists := common.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"}) return } if userID != authenticatedUserID { c.JSON(http.StatusForbidden, gin.H{"error": "cannot update other user's avatar"}) return } fileHeader, err := c.FormFile("avatar") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "no file provided"}) return } // Validate and process image resizedImage, err := h.imageService.ProcessAvatar(fileHeader) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Generate S3 key s3Key := h.imageService.GenerateS3Key(userID) // Upload to S3 (or local storage for now) avatarURL, err := h.imageService.UploadToS3(resizedImage, s3Key) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upload avatar"}) return } // Update avatar_url in DB if err := h.userService.UpdateAvatarURL(userID, avatarURL); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update avatar"}) return } c.JSON(http.StatusOK, gin.H{"avatar_url": avatarURL}) } // DeleteAvatar handles avatar deletion // T0222: Validates user_id, deletes file from S3, and sets avatar_url to NULL in DB func (h *AvatarHandler) DeleteAvatar(c *gin.Context) { userIDStr := c.Param("id") userID, err := uuid.Parse(userIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } // Check that user_id corresponds to authenticated user authenticatedUserID, exists := common.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"}) return } if userID != authenticatedUserID { c.JSON(http.StatusForbidden, gin.H{"error": "cannot delete other user's avatar"}) return } // Get current avatar_url from DB user, err := h.userService.GetByID(userID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) return } // Delete file from S3 (or local storage) if exists if user.Avatar != "" { if err := h.imageService.DeleteFromS3(user.Avatar); err != nil { // Log error but continue (file may already be deleted) // In production, you might want to use a logger here _ = err } } // Set avatar_url to empty string (NULL in DB) if err := h.userService.UpdateAvatarURL(userID, ""); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete avatar"}) return } c.JSON(http.StatusOK, gin.H{"message": "avatar deleted"}) }