1e678c4b7e
modified: serv_nginx/api_bb/go.mod modified: serv_nginx/api_bb/go.sum modified: serv_nginx/api_bb/internal/database/migrate.go modified: serv_nginx/api_bb/internal/handlers/auth.go new file: serv_nginx/api_bb/internal/handlers/email_handler.go modified: serv_nginx/api_bb/internal/handlers/handlers.go modified: serv_nginx/api_bb/internal/models/user.go new file: serv_nginx/api_bb/internal/repository/email_repository.go modified: serv_nginx/api_bb/internal/repository/user_repository.go modified: serv_nginx/api_bb/internal/routes/routes.go new file: serv_nginx/api_bb/internal/service/email_service.go modified: serv_nginx/api_bb/internal/service/user_service.go new file: serv_nginx/api_bb/pkg/email/email.go add email sender, vrificator and reset password
224 lines
7.0 KiB
Go
224 lines
7.0 KiB
Go
// 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 подтверждения отправлен повторно",
|
|
})
|
|
}
|