124 lines
3.5 KiB
Go
124 lines
3.5 KiB
Go
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"})
|
|
}
|