modified: serv_nginx/api_bb/internal/handlers/avatar.go
add loggs into all methods
This commit is contained in:
@@ -29,7 +29,13 @@ func NewAvatarHandler(avatarService service.AvatarService) *AvatarHandler {
|
|||||||
func (h *AvatarHandler) Routes() chi.Router {
|
func (h *AvatarHandler) Routes() chi.Router {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
// r.Get("/avatar/{filename}", h.ServeAvatar)
|
h.logger.Debug("Registering avatar routes",
|
||||||
|
zap.String("POST", "/upload"),
|
||||||
|
zap.String("DELETE", "/delete"),
|
||||||
|
zap.String("GET", "/{filename}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// r.Get("/avatar/{filename}", h.ServeAvatar)
|
||||||
r.Post("/upload", h.UploadAvatar)
|
r.Post("/upload", h.UploadAvatar)
|
||||||
r.Delete("/delete", h.DeleteAvatar)
|
r.Delete("/delete", h.DeleteAvatar)
|
||||||
r.Get("/{filename}", h.GetAvatar)
|
r.Get("/{filename}", h.GetAvatar)
|
||||||
@@ -38,108 +44,204 @@ func (h *AvatarHandler) Routes() chi.Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AvatarHandler) UploadAvatar(w http.ResponseWriter, r *http.Request) {
|
func (h *AvatarHandler) UploadAvatar(w http.ResponseWriter, r *http.Request) {
|
||||||
user, ok := middleware.GetUserFromContext(r.Context())
|
h.logger.Debug("UploadAvatar START",
|
||||||
if !ok {
|
zap.String("method", r.Method),
|
||||||
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
|
zap.String("path", r.URL.Path),
|
||||||
return
|
zap.String("remote_addr", r.RemoteAddr),
|
||||||
}
|
)
|
||||||
|
|
||||||
// Парсим multipart форму
|
defer func() {
|
||||||
if err := r.ParseMultipartForm(10 << 20); err != nil { // 10MB limit
|
h.logger.Debug("UploadAvatar END",
|
||||||
utils.RespondWithError(w, http.StatusBadRequest, "Failed to parse form: "+err.Error())
|
zap.String("method", r.Method),
|
||||||
return
|
zap.String("path", r.URL.Path),
|
||||||
}
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
file, header, err := r.FormFile("avatar")
|
user, ok := middleware.GetUserFromContext(r.Context())
|
||||||
if err != nil {
|
if !ok {
|
||||||
utils.RespondWithError(w, http.StatusBadRequest, "Failed to get file: "+err.Error())
|
h.logger.Warn("UploadAvatar: authentication required")
|
||||||
return
|
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
|
||||||
}
|
return
|
||||||
defer file.Close()
|
}
|
||||||
|
|
||||||
// Проверяем тип файла
|
h.logger.Debug("UploadAvatar: user authenticated",
|
||||||
allowedTypes := map[string]bool{
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
"image/jpeg": true,
|
zap.String("username", user.FirstName+user.LastName),
|
||||||
"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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Загружаем аватар
|
// Парсим multipart форму
|
||||||
avatarPath, err := h.avatarService.UploadAvatar(user.ID, file, header)
|
h.logger.Debug("UploadAvatar: parsing multipart form")
|
||||||
if err != nil {
|
if err := r.ParseMultipartForm(10 << 20); err != nil { // 10MB limit
|
||||||
h.logger.Error("Failed to upload avatar", zap.Error(err))
|
h.logger.Error("UploadAvatar: failed to parse form", zap.Error(err))
|
||||||
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to upload avatar: "+err.Error())
|
utils.RespondWithError(w, http.StatusBadRequest, "Failed to parse form: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращаем ответ с полем success
|
h.logger.Debug("UploadAvatar: getting file from form")
|
||||||
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
|
file, header, err := r.FormFile("avatar")
|
||||||
"success": true,
|
if err != nil {
|
||||||
"message": "Avatar uploaded successfully",
|
h.logger.Error("UploadAvatar: failed to get file from form", zap.Error(err))
|
||||||
"avatar": avatarPath,
|
utils.RespondWithError(w, http.StatusBadRequest, "Failed to get file: "+err.Error())
|
||||||
})
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
h.logger.Debug("UploadAvatar: file received",
|
||||||
|
zap.String("filename", header.Filename),
|
||||||
|
zap.Int64("size", header.Size),
|
||||||
|
zap.String("content_type", header.Header.Get("Content-Type")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Проверяем тип файла
|
||||||
|
allowedTypes := map[string]bool{
|
||||||
|
"image/jpeg": true,
|
||||||
|
"image/jpg": true,
|
||||||
|
"image/png": true,
|
||||||
|
"image/gif": true,
|
||||||
|
}
|
||||||
|
contentType := header.Header.Get("Content-Type")
|
||||||
|
if !allowedTypes[contentType] {
|
||||||
|
h.logger.Warn("UploadAvatar: invalid file type",
|
||||||
|
zap.String("content_type", contentType),
|
||||||
|
zap.String("filename", header.Filename),
|
||||||
|
)
|
||||||
|
utils.RespondWithError(w, http.StatusBadRequest, "Only JPEG, PNG and GIF images are allowed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.logger.Debug("UploadAvatar: file type validated successfully")
|
||||||
|
|
||||||
|
// Загружаем аватар
|
||||||
|
h.logger.Debug("UploadAvatar: calling avatarService.UploadAvatar",
|
||||||
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
|
)
|
||||||
|
avatarPath, err := h.avatarService.UploadAvatar(user.ID, file, header)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("UploadAvatar: failed to upload avatar", zap.Error(err))
|
||||||
|
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to upload avatar: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.logger.Info("UploadAvatar: avatar uploaded successfully",
|
||||||
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
|
zap.String("avatar_path", avatarPath),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Возвращаем ответ с полем 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) {
|
func (h *AvatarHandler) DeleteAvatar(w http.ResponseWriter, r *http.Request) {
|
||||||
user, ok := middleware.GetUserFromContext(r.Context())
|
h.logger.Debug("DeleteAvatar START",
|
||||||
if !ok {
|
zap.String("method", r.Method),
|
||||||
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
|
zap.String("path", r.URL.Path),
|
||||||
return
|
zap.String("remote_addr", r.RemoteAddr),
|
||||||
}
|
)
|
||||||
|
|
||||||
if err := h.avatarService.DeleteAvatar(user.ID); err != nil {
|
defer func() {
|
||||||
h.logger.Error("Failed to delete avatar", zap.Error(err))
|
h.logger.Debug("DeleteAvatar END",
|
||||||
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete avatar: "+err.Error())
|
zap.String("method", r.Method),
|
||||||
return
|
zap.String("path", r.URL.Path),
|
||||||
}
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
// Возвращаем ответ с полем success
|
user, ok := middleware.GetUserFromContext(r.Context())
|
||||||
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
|
if !ok {
|
||||||
"success": true,
|
h.logger.Warn("DeleteAvatar: authentication required")
|
||||||
"message": "Avatar deleted successfully",
|
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
|
||||||
})
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.logger.Debug("DeleteAvatar: user authenticated",
|
||||||
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
|
zap.String("username", user.FirstName+user.LastName),
|
||||||
|
)
|
||||||
|
|
||||||
|
h.logger.Debug("DeleteAvatar: calling avatarService.DeleteAvatar",
|
||||||
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
|
)
|
||||||
|
if err := h.avatarService.DeleteAvatar(user.ID); err != nil {
|
||||||
|
h.logger.Error("DeleteAvatar: failed to delete avatar", zap.Error(err))
|
||||||
|
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to delete avatar: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.logger.Info("DeleteAvatar: avatar deleted successfully",
|
||||||
|
zap.Int64("user_id", int64(user.ID)),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Возвращаем ответ с полем success
|
||||||
|
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
|
||||||
|
"success": true,
|
||||||
|
"message": "Avatar deleted successfully",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /v1/user/avatar/avatar_22_1760417314.png
|
// GET /v1/user/avatar/avatar_22_1760417314.png
|
||||||
func (h *AvatarHandler) GetAvatar(w http.ResponseWriter, r *http.Request) {
|
func (h *AvatarHandler) GetAvatar(w http.ResponseWriter, r *http.Request) {
|
||||||
filename := chi.URLParam(r, "filename")
|
filename := chi.URLParam(r, "filename")
|
||||||
|
|
||||||
h.logger.Info("handling get avatar request",
|
h.logger.Debug("GetAvatar START",
|
||||||
|
zap.String("method", r.Method),
|
||||||
|
zap.String("filename", filename),
|
||||||
|
zap.String("remote_addr", r.RemoteAddr),
|
||||||
|
zap.String("url", r.URL.String()),
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
h.logger.Debug("GetAvatar END",
|
||||||
|
zap.String("method", r.Method),
|
||||||
|
zap.String("filename", filename),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
h.logger.Info("GetAvatar: handling get avatar request",
|
||||||
zap.String("method", r.Method),
|
zap.String("method", r.Method),
|
||||||
zap.String("filename", filename),
|
zap.String("filename", filename),
|
||||||
zap.String("remote_addr", r.RemoteAddr),
|
zap.String("remote_addr", r.RemoteAddr),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Вариант 1: Используем ServeAvatarFile (более эффективно для больших файлов)
|
// Вариант 1: Используем ServeAvatarFile (более эффективно для больших файлов)
|
||||||
|
h.logger.Debug("GetAvatar: calling avatarService.ServeAvatarFile",
|
||||||
|
zap.String("filename", filename),
|
||||||
|
)
|
||||||
contentType, err := h.avatarService.ServeAvatarFile(w, filename)
|
contentType, err := h.avatarService.ServeAvatarFile(w, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Warn("failed to serve avatar file",
|
h.logger.Warn("GetAvatar: failed to serve avatar file",
|
||||||
zap.String("filename", filename),
|
zap.String("filename", filename),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case err.Error() == "avatar file not found":
|
case err.Error() == "avatar file not found":
|
||||||
|
h.logger.Warn("GetAvatar: avatar file not found", zap.String("filename", filename))
|
||||||
utils.RespondWithError(w, http.StatusNotFound, "Avatar not found")
|
utils.RespondWithError(w, http.StatusNotFound, "Avatar not found")
|
||||||
case err.Error() == "invalid filename" || err.Error() == "unsupported file format":
|
case err.Error() == "invalid filename" || err.Error() == "unsupported file format":
|
||||||
|
h.logger.Warn("GetAvatar: invalid filename or format",
|
||||||
|
zap.String("filename", filename),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
|
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
|
||||||
default:
|
default:
|
||||||
|
h.logger.Error("GetAvatar: internal server error", zap.Error(err))
|
||||||
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to serve avatar")
|
utils.RespondWithError(w, http.StatusInternalServerError, "Failed to serve avatar")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Устанавливаем заголовки
|
// Устанавливаем заголовки
|
||||||
|
h.logger.Debug("GetAvatar: setting response headers",
|
||||||
|
zap.String("content_type", contentType),
|
||||||
|
)
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
w.Header().Set("Cache-Control", "public, max-age=31536000") // Кэш на 1 год
|
w.Header().Set("Cache-Control", "public, max-age=31536000") // Кэш на 1 год
|
||||||
w.Header().Set("Expires", time.Now().Add(365*24*time.Hour).Format(http.TimeFormat))
|
w.Header().Set("Expires", time.Now().Add(365*24*time.Hour).Format(http.TimeFormat))
|
||||||
|
|
||||||
h.logger.Info("avatar served successfully",
|
h.logger.Info("GetAvatar: avatar served successfully",
|
||||||
zap.String("filename", filename),
|
zap.String("filename", filename),
|
||||||
zap.String("content_type", contentType),
|
zap.String("content_type", contentType),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user