Files
tp/serv_nginx/api_bb/internal/handlers/auth.go
T
2025-10-10 05:16:22 +05:00

254 lines
7.2 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"
"fmt"
"io"
"net/http"
"time"
"go-rest-api/internal/models"
"go-rest-api/internal/service"
"go-rest-api/pkg/middleware"
"go-rest-api/pkg/utils"
"github.com/go-chi/chi/v5"
)
type AuthHandler struct {
authService service.AuthService
jwtService service.JWTService
}
func NewAuthHandler(authService service.AuthService, jwtService service.JWTService) *AuthHandler {
return &AuthHandler{
authService: authService,
jwtService: jwtService,
}
}
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.Options("/profile", h.handleOptions)
r.Post("/register", h.Register)
r.Post("/login", h.Login)
r.Post("/logout", h.Logout)
r.Get("/profile", h.GetProfile)
return r
}
// Обработчик для OPTIONS запросов
func (h *AuthHandler) handleOptions(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Max-Age", "300")
w.WriteHeader(http.StatusOK)
}
type RegisterRequest struct {
Email string `json:"email"`
Password string `json:"password"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
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"`
}
type UserResponse struct {
ID uint `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Phone string `json:"phone"`
Experience string `json:"experience"`
Goals string `json:"goals"`
Newsletter bool `json:"newsletter"`
Role string `json:"role"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (h *AuthHandler) Register(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")
// Логируем тело запроса для отладки
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Failed to read request body: "+err.Error())
return
}
// Восстанавливаем тело для дальнейшего использования
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
fmt.Printf("Raw request body: %s\n", string(bodyBytes))
var req RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
fmt.Printf("JSON decode error: %v\n", err)
utils.RespondWithError(w, http.StatusBadRequest, "Invalid JSON payload: "+err.Error())
return
}
fmt.Printf("Parsed register request: %+v\n", req)
// Валидация обязательных полей
if req.FirstName == "" {
utils.RespondWithError(w, http.StatusBadRequest, "First name is required")
return
}
if req.LastName == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Last name is required")
return
}
if req.Email == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Email is required")
return
}
if req.Password == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Password is required")
return
}
if len(req.Password) < 6 {
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 {
fmt.Printf("Auth service error: %v\n", err)
utils.RespondWithError(w, http.StatusBadRequest, err.Error())
return
}
// После успешной регистрации возвращаем данные пользователя
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) {
// Устанавливаем CORS заголовки
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Credentials", "true")
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
utils.RespondWithError(w, http.StatusBadRequest, "Invalid request payload: "+err.Error())
return
}
// Валидация
if req.Email == "" || req.Password == "" {
utils.RespondWithError(w, http.StatusBadRequest, "Email and password are required")
return
}
user, token, err := h.authService.Login(req.Email, req.Password)
if err != nil {
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),
})
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")
// Удаляем куку
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,
})
utils.RespondWithJSON(w, http.StatusOK, map[string]string{
"message": "Logout successful",
})
}
func (h *AuthHandler) GetProfile(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")
user, ok := middleware.GetUserFromContext(r.Context())
if !ok {
utils.RespondWithError(w, http.StatusUnauthorized, "Authentication required")
return
}
utils.RespondWithJSON(w, http.StatusOK, toUserResponse(user))
}
func toUserResponse(user *models.User) UserResponse {
return UserResponse{
ID: user.ID,
Email: user.Email,
FirstName: user.FirstName,
LastName: user.LastName,
Phone: user.Phone,
Experience: user.Experience,
Goals: user.Goals,
Newsletter: user.Newsletter,
Role: user.Role,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
}