Files
tp/serv_nginx/api_bb/internal/handlers/auth.go
T

253 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// handlers/auth.go
package handlers
import (
"bytes"
"encoding/json"
"io"
"net/http"
"time"
"api_bb/internal/models"
"api_bb/internal/service"
"api_bb/pkg/logger"
"api_bb/pkg/utils"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type AuthHandler struct {
authService service.AuthService
jwtService service.JWTService
logger logger.Interface
}
func NewAuthHandler(authService service.AuthService, jwtService service.JWTService) *AuthHandler {
return &AuthHandler{
authService: authService,
jwtService: jwtService,
logger: logger.NewWrapper(logger.Get().With(zap.String("handler", "auth"))),
}
}
func (h *AuthHandler) Routes() chi.Router {
r := chi.NewRouter()
// Обработка OPTIONS запросов для CORS
r.Options("/register", h.handleOptions)
r.Options("/login", h.handleOptions)
r.Options("/logout", h.handleOptions)
r.Post("/register", h.Register)
r.Post("/login", h.Login)
r.Post("/logout", h.Logout)
return r
}
// Обработчик для OPTIONS запросов
func (h *AuthHandler) handleOptions(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
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),
)
// После успешной регистрации возвращаем данные пользователя
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),
)
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.logger.Error("failed to decode login request", zap.Error(err))
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if req.Email == "" || req.Password == "" {
h.logger.Warn("login failed - email or password empty")
utils.RespondWithError(w, http.StatusBadRequest, "Email and password are required")
return
}
h.logger.Info("attempting user 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, // В production установить true
SameSite: http.SameSiteLaxMode,
Expires: time.Now().Add(24 * time.Hour),
})
h.logger.Info("user logged in successfully",
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",
})
}