15357fd3c0
yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarbacreate and moove into new directories for BegushiyBashkir and yalarba
267 lines
7.6 KiB
Go
267 lines
7.6 KiB
Go
// handlers/auth.go
|
|
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"api_bb/internal/models"
|
|
"api_bb/internal/service"
|
|
"api_bb/pkg/logger"
|
|
"api_bb/pkg/utils"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type AuthHandler struct {
|
|
authService service.AuthService
|
|
jwtService service.JWTService
|
|
logger logger.LoggerInterface
|
|
emailService service.EmailService
|
|
}
|
|
|
|
func NewAuthHandler(authService service.AuthService, jwtService service.JWTService, emailService service.EmailService) *AuthHandler {
|
|
return &AuthHandler{
|
|
authService: authService,
|
|
jwtService: jwtService,
|
|
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "auth"))),
|
|
emailService: emailService,
|
|
}
|
|
}
|
|
|
|
type RegisterRequest struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
FirstName string `json:"firstName"`
|
|
LastName string `json:"lastName"`
|
|
Phone string `json:"phone"`
|
|
Experience string `json:"experience"`
|
|
Goals string `json:"goals"`
|
|
Newsletter bool `json:"newsletter"`
|
|
}
|
|
|
|
type LoginRequest struct {
|
|
Email string `json:"email"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
|
|
|
|
h.logger.Info("handling register request",
|
|
zap.String("method", r.Method),
|
|
zap.String("path", r.URL.Path),
|
|
zap.String("remote_addr", r.RemoteAddr),
|
|
)
|
|
|
|
// Логируем тело запроса для отладки
|
|
bodyBytes, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
h.logger.Error("failed to read request body", zap.Error(err))
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Восстанавливаем тело для дальнейшего использования
|
|
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
|
|
|
h.logger.Debug("raw register request body", zap.String("body", string(bodyBytes)))
|
|
|
|
var req RegisterRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
h.logger.Error("failed to decode JSON payload", zap.Error(err))
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON payload: "+err.Error())
|
|
return
|
|
}
|
|
|
|
h.logger.Info("parsed register request",
|
|
zap.String("email", req.Email),
|
|
zap.String("first_name", req.FirstName),
|
|
zap.String("last_name", req.LastName),
|
|
)
|
|
|
|
// Валидация обязательных полей
|
|
if req.FirstName == "" {
|
|
h.logger.Warn("register failed - first name required")
|
|
utils.RespondWithError(w, http.StatusBadRequest, "First name is required")
|
|
return
|
|
}
|
|
if req.LastName == "" {
|
|
h.logger.Warn("register failed - last name required")
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Last name is required")
|
|
return
|
|
}
|
|
if req.Email == "" {
|
|
h.logger.Warn("register failed - email required")
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Email is required")
|
|
return
|
|
}
|
|
if req.Password == "" {
|
|
h.logger.Warn("register failed - password required")
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Password is required")
|
|
return
|
|
}
|
|
if len(req.Password) < 6 {
|
|
h.logger.Warn("register failed - password too short")
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Password must be at least 6 characters")
|
|
return
|
|
}
|
|
|
|
user := &models.User{
|
|
Email: req.Email,
|
|
Password: req.Password,
|
|
FirstName: req.FirstName,
|
|
LastName: req.LastName,
|
|
Phone: req.Phone,
|
|
Experience: req.Experience,
|
|
Goals: req.Goals,
|
|
Newsletter: req.Newsletter,
|
|
Role: "user",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
if err := h.authService.Register(user); err != nil {
|
|
h.logger.Error("auth service registration failed",
|
|
zap.String("email", req.Email),
|
|
zap.Error(err),
|
|
)
|
|
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
h.logger.Info("user registered successfully",
|
|
zap.Uint("user_id", user.ID),
|
|
zap.String("email", user.Email),
|
|
)
|
|
|
|
// Отправки сообщения для верификации Email
|
|
if err := h.emailService.SendVerificationEmail(user.ID, user.Email, user.FirstName); err != nil {
|
|
h.logger.Error("failed to send verification email",
|
|
zap.Error(err),
|
|
zap.Uint("user_id", user.ID))
|
|
}
|
|
|
|
// После успешной регистрации возвращаем данные пользователя
|
|
utils.RespondWithJSON(w, http.StatusCreated, map[string]interface{}{
|
|
"message": "User registered successfully",
|
|
"user": toUserResponse(user),
|
|
})
|
|
}
|
|
|
|
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
|
|
h.logger.Info("handling login request",
|
|
zap.String("method", r.Method),
|
|
zap.String("path", r.URL.Path),
|
|
zap.String("remote_addr", r.RemoteAddr),
|
|
)
|
|
|
|
// Проверяем Content-Type
|
|
if r.Header.Get("Content-Type") != "application/json" {
|
|
h.logger.Warn("invalid content type", zap.String("content_type", r.Header.Get("Content-Type")))
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Content-Type must be application/json")
|
|
return
|
|
}
|
|
|
|
// Читаем и логируем тело запроса
|
|
bodyBytes, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
h.logger.Error("failed to read request body", zap.Error(err))
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body")
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
// Восстанавливаем тело
|
|
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
|
|
|
h.logger.Debug("request body", zap.String("body", string(bodyBytes)))
|
|
|
|
var req LoginRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
h.logger.Error("JSON decode failed",
|
|
zap.Error(err),
|
|
zap.String("raw_body", string(bodyBytes)),
|
|
)
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON: "+err.Error())
|
|
return
|
|
}
|
|
|
|
req.Email = strings.TrimSpace(req.Email)
|
|
req.Password = strings.TrimSpace(req.Password)
|
|
|
|
// Валидация
|
|
if req.Email == "" || req.Password == "" {
|
|
h.logger.Warn("validation failed",
|
|
zap.String("email", req.Email),
|
|
zap.Int("password_len", len(req.Password)),
|
|
)
|
|
utils.RespondWithError(w, http.StatusBadRequest, "Email and password are required")
|
|
return
|
|
}
|
|
|
|
h.logger.Info("attempting login", zap.String("email", req.Email))
|
|
|
|
user, token, err := h.authService.Login(req.Email, req.Password)
|
|
if err != nil {
|
|
h.logger.Warn("login failed", zap.String("email", req.Email), zap.Error(err))
|
|
utils.RespondWithError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
|
|
// Устанавливаем куки
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "auth_token",
|
|
Value: token,
|
|
Path: "/",
|
|
HttpOnly: true,
|
|
Secure: false,
|
|
SameSite: http.SameSiteLaxMode,
|
|
Expires: time.Now().Add(24 * time.Hour),
|
|
})
|
|
|
|
h.logger.Info("login successful",
|
|
zap.Uint("user_id", user.ID),
|
|
zap.String("email", user.Email),
|
|
)
|
|
|
|
utils.RespondWithJSON(w, http.StatusOK, map[string]interface{}{
|
|
"message": "Login successful",
|
|
"token": token,
|
|
"user": toUserResponse(user),
|
|
})
|
|
}
|
|
|
|
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
|
// Устанавливаем CORS заголовки
|
|
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
|
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
|
|
h.logger.Info("handling logout request",
|
|
zap.String("method", r.Method),
|
|
zap.String("path", r.URL.Path),
|
|
zap.String("remote_addr", r.RemoteAddr),
|
|
)
|
|
|
|
// Удаляем куку
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "auth_token",
|
|
Value: "",
|
|
Path: "/",
|
|
HttpOnly: true,
|
|
Secure: false,
|
|
SameSite: http.SameSiteLaxMode,
|
|
Expires: time.Now().Add(-1 * time.Hour),
|
|
MaxAge: -1,
|
|
})
|
|
|
|
h.logger.Info("user logged out successfully")
|
|
|
|
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
|
|
"message": "Logout successful",
|
|
})
|
|
}
|