// handlers/email_handler.go package handlers import ( "net/http" "api_bb/internal/models" "api_bb/internal/service" "api_bb/pkg/logger" "api_bb/pkg/middleware" "api_bb/pkg/utils" "go.uber.org/zap" ) type EmailHandler struct { logger logger.LoggerInterface emailService *service.EmailService } func NewEmailHandler(emailService *service.EmailService) *EmailHandler { return &EmailHandler{ logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "email"))), emailService: emailService, } } // VerifyEmail подтверждает email пользователя func (h *EmailHandler) VerifyEmail(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling email verification request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) token := r.URL.Query().Get("token") if token == "" { h.logger.Warn("email verification failed - token is required") utils.RespondWithError(w, http.StatusBadRequest, "Токен обязателен") return } if err := h.emailService.VerifyEmail(token); err != nil { h.logger.Error("email verification failed, expired", zap.Error(err), zap.String("token", token), ) utils.RespondWithError(w, http.StatusBadRequest, "Неверный или просроченный токен") return } h.logger.Info("email successfully verified", zap.String("token", token), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Email успешно подтвержден", }) } // RequestPasswordReset запрашивает сброс пароля func (h *EmailHandler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling password reset request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) var req models.PasswordResetRequest if err := utils.DecodeJSONBody(w, r, &req); err != nil { h.logger.Warn("password reset request failed - invalid request format", zap.Error(err), ) utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса") return } if err := h.emailService.SendPasswordResetEmail(req.Email); err != nil { h.logger.Error("password reset request failed", zap.Error(err), zap.String("email", req.Email), ) // Для безопасности всегда возвращаем успех } h.logger.Info("password reset request processed", zap.String("email", req.Email), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Если email зарегистрирован, инструкции по восстановлению пароля будут отправлены", }) } // ConfirmPasswordReset подтверждает сброс пароля func (h *EmailHandler) ConfirmPasswordReset(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling password reset confirmation request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) var req models.PasswordResetConfirm if err := utils.DecodeJSONBody(w, r, &req); err != nil { h.logger.Warn("password reset confirmation failed - invalid request format", zap.Error(err), ) utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса") return } if err := h.emailService.ResetPassword(req.Token, req.Password); err != nil { h.logger.Error("password reset confirmation failed", zap.Error(err), zap.String("token", req.Token), ) utils.RespondWithError(w, http.StatusBadRequest, "Неверный или просроченный токен") return } h.logger.Info("password successfully reset", zap.String("token", req.Token), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Пароль успешно изменен", }) } type NewsletterRequest struct { Subject string `json:"subject" validate:"required"` Content string `json:"content" validate:"required"` } // SendNewsletter отправляет рассылку новостей func (h *EmailHandler) SendNewsletter(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling newsletter sending request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) var req NewsletterRequest if err := utils.DecodeJSONBody(w, r, &req); err != nil { h.logger.Warn("newsletter sending failed - invalid request format", zap.Error(err), ) utils.RespondWithError(w, http.StatusBadRequest, "Неверный формат запроса") return } if err := h.emailService.SendNewsletterToSubscribers(req.Subject, req.Content); err != nil { h.logger.Error("newsletter sending failed", zap.Error(err), zap.String("subject", req.Subject), ) utils.RespondWithError(w, http.StatusInternalServerError, "Не удалось отправить рассылку") return } h.logger.Info("newsletter sent successfully", zap.String("subject", req.Subject), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Рассылка отправлена подписчикам", }) } // ResendVerification повторно отправляет email верификации func (h *EmailHandler) ResendVerification(w http.ResponseWriter, r *http.Request) { h.logger.Info("handling resend verification request", zap.String("method", r.Method), zap.String("path", r.URL.Path), zap.String("remote_addr", r.RemoteAddr), ) user, ok := middleware.GetUserFromContext(r.Context()) if !ok { h.logger.Warn("resend verification failed - authentication required") utils.RespondWithError(w, http.StatusUnauthorized, "Пользователь не авторизован") return } // Получаем пользователя userData, err := h.emailService.GetUserByID(user.ID) if err != nil { h.logger.Warn("resend verification failed - user not found", zap.Uint("user_id", user.ID), zap.Error(err), ) utils.RespondWithError(w, http.StatusNotFound, "Пользователь не найден") return } if userData.EmailVerified { h.logger.Warn("resend verification failed - email already verified", zap.Uint("user_id", user.ID), zap.String("email", userData.Email), ) utils.RespondWithError(w, http.StatusBadRequest, "Email уже подтвержден") return } if err := h.emailService.SendVerificationEmail(userData.ID, userData.Email, userData.FirstName); err != nil { h.logger.Error("resend verification failed", zap.Error(err), zap.Uint("user_id", user.ID), zap.String("email", userData.Email), ) utils.RespondWithError(w, http.StatusInternalServerError, "Не удалось отправить email подтверждения") return } h.logger.Info("verification email resent successfully", zap.Uint("user_id", user.ID), zap.String("email", userData.Email), ) utils.RespondWithJSON(w, http.StatusOK, map[string]string{ "message": "Email подтверждения отправлен повторно", }) }