Files
tp/serv_nginx/api_bb/internal/handlers/avatar.go
T
valitovgaziz bbf470617b modified: serv_nginx/api_bb/internal/handlers/avatar.go
modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/handlers/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/avatar_service.go
	modified:   serv_nginx/nginx/nginx-ssl.conf
try to serve file name throught path
2025-10-14 12:41:16 +05:00

147 lines
4.5 KiB
Go

// handlers/avatar.go
package handlers
import (
"net/http"
"time"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/middleware"
"api_bb/pkg/utils"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type AvatarHandler struct {
logger logger.LoggerInterface
avatarService service.AvatarService
}
func NewAvatarHandler(avatarService service.AvatarService) *AvatarHandler {
return &AvatarHandler{
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "avatar"))),
avatarService: avatarService,
}
}
func (h *AvatarHandler) Routes() chi.Router {
r := chi.NewRouter()
// r.Get("/avatar/{filename}", h.ServeAvatar)
r.Post("/upload", h.UploadAvatar)
r.Delete("/delete", h.DeleteAvatar)
r.Get("/{filename}", h.GetAvatar)
return r
}
func (h *AvatarHandler) UploadAvatar(w http.ResponseWriter, r *http.Request) {
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
// Парсим multipart форму
if err := r.ParseMultipartForm(10 << 20); err != nil { // 10MB limit
utils.RespondWithError(w, http.StatusBadRequest, "Failed to parse form: "+err.Error())
return
}
file, header, err := r.FormFile("avatar")
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Failed to get file: "+err.Error())
return
}
defer file.Close()
// Проверяем тип файла
allowedTypes := map[string]bool{
"image/jpeg": true,
"image/jpg": true,
"image/png": true,
"image/gif": true,
}
if !allowedTypes[header.Header.Get("Content-Type")] {
utils.RespondWithError(w, http.StatusBadRequest, "Only JPEG, PNG and GIF images are allowed")
return
}
// Загружаем аватар
avatarPath, err := h.avatarService.UploadAvatar(user.ID, file, header)
if err != nil {
h.logger.Error("Failed to upload avatar", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to upload avatar: "+err.Error())
return
}
// Возвращаем ответ с полем success
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"success": true,
"message": "Avatar uploaded successfully",
"avatar": avatarPath,
})
}
func (h *AvatarHandler) DeleteAvatar(w http.ResponseWriter, r *http.Request) {
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
if err := h.avatarService.DeleteAvatar(user.ID); err != nil {
h.logger.Error("Failed to delete avatar", zap.Error(err))
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete avatar: "+err.Error())
return
}
// Возвращаем ответ с полем success
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
"success": true,
"message": "Avatar deleted successfully",
})
}
// GET /v1/user/avatar/avatar_22_1760417314.png
func (h *AvatarHandler) GetAvatar(w http.ResponseWriter, r *http.Request) {
filename := chi.URLParam(r, "filename")
h.logger.Info("handling get avatar request",
zap.String("method", r.Method),
zap.String("filename", filename),
zap.String("remote_addr", r.RemoteAddr),
)
// Вариант 1: Используем ServeAvatarFile (более эффективно для больших файлов)
contentType, err := h.avatarService.ServeAvatarFile(w, filename)
if err != nil {
h.logger.Warn("failed to serve avatar file",
zap.String("filename", filename),
zap.Error(err),
)
switch {
case err.Error() == "avatar file not found":
utils.RespondWithError(w, http.StatusNotFound, "Avatar not found")
case err.Error() == "invalid filename" || err.Error() == "unsupported file format":
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
default:
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to serve avatar")
}
return
}
// Устанавливаем заголовки
w.Header().Set("Content-Type", contentType)
w.Header().Set("Cache-Control", "public, max-age=31536000") // Кэш на 1 год
w.Header().Set("Expires", time.Now().Add(365*24*time.Hour).Format(http.TimeFormat))
h.logger.Info("avatar served successfully",
zap.String("filename", filename),
zap.String("content_type", contentType),
)
}