Files
tp/main_dc/yalarba/api_yal/internal/domain/auth/handler.go
T
valitovgaziz 8b40d1bfe5 On branch main
modified:   internal/domain/auth/dto.go
	modified:   internal/domain/auth/handler.go
	modified:   internal/domain/auth/router.go
	modified:   internal/domain/auth/servcie.go
	modified:   internal/middleware/auth.go
	modified:   internal/router/router.go
auth implemented without reset password
2026-03-31 04:22:54 +05:00

329 lines
10 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.
// handler.go
package auth
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"api_yal/internal/logger"
"api_yal/internal/middleware"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
)
// Cookie константы
const (
RefreshTokenCookieName = "refresh_token"
RefreshTokenExpiration = 7 * 24 * time.Hour // 7 дней
)
// AuthHandler обработчик для аутентификации
type AuthHandler struct {
authService AuthService
validator *validator.Validate
}
// NewAuthHandler создает новый экземпляр AuthHandler
func NewAuthHandler(authService AuthService) *AuthHandler {
return &AuthHandler{
authService: authService,
validator: validator.New(),
}
}
// setRefreshTokenCookie устанавливает HttpOnly cookie с refresh token
func (h *AuthHandler) setRefreshTokenCookie(w http.ResponseWriter, refreshToken string) {
http.SetCookie(w, &http.Cookie{
Name: RefreshTokenCookieName,
Value: refreshToken,
Path: "/",
HttpOnly: true,
Secure: true, // Всегда true в production
SameSite: http.SameSiteStrictMode,
MaxAge: int(RefreshTokenExpiration.Seconds()),
})
}
// clearRefreshTokenCookie удаляет refresh token cookie
func (h *AuthHandler) clearRefreshTokenCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: RefreshTokenCookieName,
Value: "",
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
MaxAge: -1,
})
}
// Register регистрация аккаунта пользователя
func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Info("Начало обработки запроса регистрации")
var req RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
var invalidValidationError *validator.InvalidValidationError
if errors.As(err, &invalidValidationError) {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
var errs []string
for _, err := range err.(validator.ValidationErrors) {
errs = append(errs, fmt.Sprintf("field %s is invalid: %s", err.Field(), err.Tag()))
}
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "Validation failed",
"fields": errs,
})
return
}
response, err := h.authService.Register(req)
if err != nil {
l.Error("Ошибка регистрации: %v", zap.Error(err))
status := http.StatusInternalServerError
message := "Registration failed"
if errors.Is(err, ErrUserAlreadyExists) {
status = http.StatusConflict
message = "User with this email already exists"
}
http.Error(w, message, status)
return
}
l.Info("Завершение обработки запроса регистрации")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}
// Login вход пользователя
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Debug("Начало обработки запроса входа")
var req LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.validator.Struct(req); err != nil {
var invalidValidationError *validator.InvalidValidationError
if errors.As(err, &invalidValidationError) {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
var errs []string
for _, err := range err.(validator.ValidationErrors) {
errs = append(errs, fmt.Sprintf("field %s is invalid: %s", err.Field(), err.Tag()))
}
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "Validation failed",
"fields": errs,
})
return
}
response, refreshToken, err := h.authService.Login(req)
if err != nil {
l.Error("Ошибка входа: %v", zap.Error(err))
status := http.StatusUnauthorized
message := "Login failed"
if errors.Is(err, ErrUserNotFound) {
message = "User not found"
} else if errors.Is(err, ErrInvalidPassword) {
message = "Invalid password"
}
http.Error(w, message, status)
return
}
// Устанавливаем refresh token в HttpOnly cookie
h.setRefreshTokenCookie(w, refreshToken)
l.Debug("Завершение обработки запроса входа")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
// RefreshToken обновление токена
// Поддерживает два способа получения refresh token:
// 1. Из HttpOnly cookie (для web приложений)
// 2. Из тела запроса (для мобильных приложений)
func (h *AuthHandler) RefreshToken(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Info("Начало обработки запроса обновления токена")
var refreshToken string
// Пытаемся получить refresh token из cookie
cookie, err := r.Cookie(RefreshTokenCookieName)
if err == nil && cookie != nil {
refreshToken = cookie.Value
}
// Если в cookie нет, пробуем получить из тела запроса (для мобильных приложений)
if refreshToken == "" {
var req RefreshTokenRequest
if err := json.NewDecoder(r.Body).Decode(&req); err == nil {
refreshToken = req.RefreshToken
}
}
if refreshToken == "" {
http.Error(w, "Refresh token required", http.StatusBadRequest)
return
}
response, err := h.authService.RefreshToken(refreshToken)
if err != nil {
l.Error("Ошибка обновления токена", zap.Error(err))
if errors.Is(err, ErrInvalidToken) || errors.Is(err, ErrTokenExpired) {
// Очищаем невалидный refresh token
h.clearRefreshTokenCookie(w)
http.Error(w, "Invalid or expired refresh token", http.StatusUnauthorized)
return
}
http.Error(w, "Token refresh failed", http.StatusUnauthorized)
return
}
// Генерируем новый refresh token
newRefreshToken, err := h.generateNewRefreshTokenFromUser(response.User.ID)
if err != nil {
l.Error("Ошибка генерации нового refresh token", zap.Error(err))
http.Error(w, "Failed to generate refresh token", http.StatusInternalServerError)
return
}
// Обновляем refresh token в cookie
h.setRefreshTokenCookie(w, newRefreshToken)
l.Info("Завершение обработки запроса обновления токена")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
// generateNewRefreshTokenFromUser генерирует новый refresh token для пользователя
// Вспомогательная функция для обновления refresh token
func (h *AuthHandler) generateNewRefreshTokenFromUser(userID uint) (string, error) {
// Используем сервис для генерации refresh token
return h.authService.GenerateNewRefreshToken(userID)
}
// Logout выход пользователя
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Info("Начало обработки запроса выхода")
// Получаем ID пользователя из контекста (устанавливается middleware)
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if err := h.authService.Logout(userID); err != nil {
l.Error("Ошибка выхода: %v", zap.Error(err))
http.Error(w, "Logout failed", http.StatusInternalServerError)
return
}
// Очищаем refresh token cookie
h.clearRefreshTokenCookie(w)
l.Info("Завершение обработки запроса выхода")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"message": "Successfully logged out",
})
}
// GetProfile получение профиля пользователя
func (h *AuthHandler) GetProfile(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Debug("Получение профиля пользователя")
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// TODO: Реализовать получение профиля через сервис
// response, err := h.authService.GetProfile(userID)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"user_id": userID,
"message": "Profile endpoint - to be implemented",
})
}
// UpdateProfile обновление профиля пользователя
func (h *AuthHandler) UpdateProfile(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Debug("Обновление профиля пользователя")
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// TODO: Реализовать обновление профиля
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"user_id": userID,
"message": "Update profile endpoint - to be implemented",
})
}
// ChangePassword смена пароля
func (h *AuthHandler) ChangePassword(w http.ResponseWriter, r *http.Request) {
l := logger.Get()
l.Debug("Смена пароля пользователя")
// Получаем ID пользователя из контекста
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// TODO: Реализовать смену пароля
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"user_id": userID,
"message": "Change password endpoint - to be implemented",
})
}