chore: remove direct avatar upload endpoint (POST /profile/avatar);

feat: add endpoints for presigned upload URLs (GET /upload/avatar, GET /upload/image);
refactor: replace ProfileDto with NewProfileDto in update profile endpoint;
feat: implement S3 integration for avatar management;
fix: update database queries to handle new avatar upload flow;
chore: add new dependencies for S3 handling (golang.org/x/time);
refactor: rename UploadService to S3Service;
refactor: change return type for func LocalizeS3Url(originalURL string) (*url.URL, error);
feat: add custom validator for upload_id
This commit is contained in:
2025-08-01 04:34:06 +03:00
parent 8dba0f79aa
commit 669349e020
15 changed files with 405 additions and 222 deletions

View File

@@ -24,7 +24,6 @@ import (
"easywish/internal/utils/enums"
"errors"
"net/http"
"os"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@@ -78,13 +77,6 @@ func NewProfileController(_log *zap.Logger, _ps services.ProfileService) Control
Middleware: []gin.HandlerFunc{},
Function: ctrl.updateProfileSettings,
},
{
HttpMethod: POST,
Path: "/avatar",
Authorization: enums.UserRole,
Middleware: []gin.HandlerFunc{},
Function: ctrl.uploadAvatar,
},
},
}
}
@@ -169,11 +161,11 @@ func (ctrl *ProfileController) getProfileSettings(c *gin.Context) {
// @Accept json
// @Produce json
// @Security JWT
// @Param request body dto.ProfileDto true " "
// @Param request body dto.NewProfileDto true " "
// @Success 200 {object} bool " "
// @Router /profile [put]
func (ctrl *ProfileController) updateProfile(c *gin.Context) {
request, err := GetRequest[dto.ProfileDto](c); if err != nil {
request, err := GetRequest[dto.NewProfileDto](c); if err != nil {
return
}
@@ -206,33 +198,3 @@ func (ctrl *ProfileController) updateProfileSettings(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
// XXX: untested
// @Summary Upload an avatar
// @Tags Profile
// @Accept mpfd
// @Produce json
// @Security JWT
// @Param file formData file true "Avatar image file"
// @Success 200 {object} dto.UrlDto "Uploaded image url"
// @Router /profile/avatar [post]
func (ctrl *ProfileController) uploadAvatar(c *gin.Context) {
cinfo := GetClientInfo(c)
allowedTypes := map[string]bool{
"image/jpeg": true,
"image/png": true,
"image/webp": true,
}
fileName, err := GetFile(c, "file", 8*1024*1024, allowedTypes); if err != nil {
return
}
defer os.Remove(*fileName)
link, err := ctrl.ps.UploadAvatar(cinfo, *fileName); if err != nil {
c.Status(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, dto.UrlDto{Url: *link})
}

View File

@@ -30,13 +30,13 @@ import (
"golang.org/x/time/rate"
)
type UploadController struct {
type S3Controller struct {
log *zap.Logger
us services.UploadService
s3 services.S3Service
}
func NewUploadController(_log *zap.Logger, _us services.UploadService) Controller {
ctrl := UploadController{log: _log, us: _us}
func NewS3Controller(_log *zap.Logger, _us services.S3Service) Controller {
ctrl := S3Controller{log: _log, s3: _us}
return &controllerImpl{
Path: "/upload",
@@ -71,8 +71,8 @@ func NewUploadController(_log *zap.Logger, _us services.UploadService) Controlle
// @Success 200 {object} models.PresignedUploadResponse "Presigned URL and form data"
// @Failure 500 "Internal server error"
// @Router /upload/avatar [get]
func (ctrl *UploadController) getAvatarUploadUrl(c *gin.Context) {
url, formData, err := ctrl.us.GetAvatarUrl()
func (ctrl *S3Controller) getAvatarUploadUrl(c *gin.Context) {
url, formData, err := ctrl.s3.CreateAvatarUrl()
if err != nil {
ctrl.log.Error("Failed to generate avatar upload URL", zap.Error(err))
c.Status(http.StatusInternalServerError)
@@ -94,8 +94,8 @@ func (ctrl *UploadController) getAvatarUploadUrl(c *gin.Context) {
// @Success 200 {object} models.PresignedUploadResponse "Presigned URL and form data"
// @Failure 500 "Internal server error"
// @Router /upload/image [get]
func (ctrl *UploadController) getImageUploadUrl(c *gin.Context) {
url, formData, err := ctrl.us.GetImageUrl()
func (ctrl *S3Controller) getImageUploadUrl(c *gin.Context) {
url, formData, err := ctrl.s3.CreateImageUrl()
if err != nil {
c.Status(http.StatusInternalServerError)
return