package handler import ( "context" "io" "net/http" "eslogad-be/internal/appcontext" "eslogad-be/internal/contract" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type FileService interface { UploadProfileAvatar(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, error) UploadDocument(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error) } type FileHandler struct { service FileService } func NewFileHandler(service FileService) *FileHandler { return &FileHandler{service: service} } func (h *FileHandler) UploadProfileAvatar(c *gin.Context) { appCtx := appcontext.FromGinContext(c.Request.Context()) if appCtx.UserID == uuid.Nil { c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized}) return } file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest}) return } defer file.Close() content, err := io.ReadAll(io.LimitReader(file, 10<<20)) if err != nil { c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest}) return } ct := header.Header.Get("Content-Type") url, err := h.service.UploadProfileAvatar(c.Request.Context(), appCtx.UserID, header.Filename, content, ct) if err != nil { c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError}) return } c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url})) } func (h *FileHandler) UploadDocument(c *gin.Context) { appCtx := appcontext.FromGinContext(c.Request.Context()) if appCtx.UserID == uuid.Nil { c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized}) return } file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest}) return } defer file.Close() content, err := io.ReadAll(io.LimitReader(file, 20<<20)) if err != nil { c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest}) return } ct := header.Header.Get("Content-Type") url, key, err := h.service.UploadDocument(c.Request.Context(), appCtx.UserID, header.Filename, content, ct) if err != nil { c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError}) return } c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key})) }