modified: serv_nginx/api_bb/.env
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
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
// 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 подтверждения отправлен повторно",
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user