package handler import ( "github.com/go-playground/validator/v10" "github.com/labstack/echo/v4" "tm/internal/domain" "tm/pkg/logger" "tm/pkg/response" ) // AuthHandler handles authentication related HTTP requests (legacy/generic) // This handler provides backward compatibility and can handle both customer and user authentication type AuthHandler struct { customerAuthService domain.CustomerAuthService userAuthService domain.UserAuthService validator *validator.Validate logger logger.Logger } // NewAuthHandler creates a new authentication handler func NewAuthHandler( customerAuthService domain.CustomerAuthService, userAuthService domain.UserAuthService, validator *validator.Validate, logger logger.Logger, ) *AuthHandler { return &AuthHandler{ customerAuthService: customerAuthService, userAuthService: userAuthService, validator: validator, logger: logger, } } // Register handles user registration (defaults to customer registration) // @Summary Register new user // @Description Create a new user account (defaults to customer) // @Tags auth // @Accept json // @Produce json // @Param request body domain.RegisterRequest true "Registration request" // @Success 201 {object} response.APIResponse{data=domain.AuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 409 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /auth/register [post] func (h *AuthHandler) Register(c echo.Context) error { var req domain.RegisterRequest // Bind request body if err := c.Bind(&req); err != nil { h.logger.Warn("Failed to bind register request", map[string]interface{}{ "error": err.Error(), }) return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { h.logger.Warn("Register request validation failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) return response.ValidationError(c, "Validation failed", err.Error()) } // Convert to customer registration request customerReq := domain.CustomerRegisterRequest{ Email: req.Email, Password: req.Password, FirstName: req.FirstName, LastName: req.LastName, CompanyID: req.CompanyID, } // Call customer service (default registration is for customers) customerAuthResponse, err := h.customerAuthService.Register(c.Request().Context(), customerReq) if err != nil { h.logger.Error("Registration failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) if err.Error() == "customer already exists" { return response.Conflict(c, "User already exists") } return response.InternalServerError(c, "Registration failed") } // Convert to generic auth response for backward compatibility authResponse := &domain.AuthResponse{ Customer: customerAuthResponse.Customer, AccessToken: customerAuthResponse.AccessToken, RefreshToken: customerAuthResponse.RefreshToken, ExpiresIn: customerAuthResponse.ExpiresIn, } return response.Created(c, authResponse, "User registered successfully") } // Login handles user authentication (tries both customer and panel user) // @Summary User login // @Description Authenticate user and return tokens // @Tags auth // @Accept json // @Produce json // @Param request body domain.LoginRequest true "Login request" // @Success 200 {object} response.APIResponse{data=domain.AuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /auth/login [post] func (h *AuthHandler) Login(c echo.Context) error { var req domain.LoginRequest // Bind request body if err := c.Bind(&req); err != nil { h.logger.Warn("Failed to bind login request", map[string]interface{}{ "error": err.Error(), }) return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { h.logger.Warn("Login request validation failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) return response.ValidationError(c, "Validation failed", err.Error()) } // Try customer login first if customerAuthResponse, err := h.customerAuthService.Login(c.Request().Context(), req); err == nil { h.logger.Info("Customer logged in successfully", map[string]interface{}{ "customer_id": customerAuthResponse.Customer.ID.String(), "email": customerAuthResponse.Customer.Email, }) // Convert to generic auth response authResponse := &domain.AuthResponse{ Customer: customerAuthResponse.Customer, AccessToken: customerAuthResponse.AccessToken, RefreshToken: customerAuthResponse.RefreshToken, ExpiresIn: customerAuthResponse.ExpiresIn, } return response.Success(c, authResponse, "Login successful") } // Try panel user login if userAuthResponse, err := h.userAuthService.Login(c.Request().Context(), req); err == nil { h.logger.Info("Panel user logged in successfully", map[string]interface{}{ "user_id": userAuthResponse.User.ID.String(), "email": userAuthResponse.User.Email, "role": userAuthResponse.User.Role, }) // Convert to generic auth response authResponse := &domain.AuthResponse{ User: userAuthResponse.User, AccessToken: userAuthResponse.AccessToken, RefreshToken: userAuthResponse.RefreshToken, ExpiresIn: userAuthResponse.ExpiresIn, } return response.Success(c, authResponse, "Login successful") } h.logger.Warn("Login failed for both customer and user", map[string]interface{}{ "email": req.Email, }) return response.Unauthorized(c, "Invalid email or password") } // RefreshToken handles token refresh (tries both customer and panel user) // @Summary Refresh access token // @Description Generate new access token using refresh token // @Tags auth // @Accept json // @Produce json // @Param request body RefreshTokenRequest true "Refresh token request" // @Success 200 {object} response.APIResponse{data=domain.AuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /auth/refresh [post] func (h *AuthHandler) RefreshToken(c echo.Context) error { var req struct { RefreshToken string `json:"refresh_token" validate:"required"` } // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Try customer token refresh first if customerAuthResponse, err := h.customerAuthService.RefreshToken(c.Request().Context(), req.RefreshToken); err == nil { // Convert to generic auth response authResponse := &domain.AuthResponse{ Customer: customerAuthResponse.Customer, AccessToken: customerAuthResponse.AccessToken, RefreshToken: customerAuthResponse.RefreshToken, ExpiresIn: customerAuthResponse.ExpiresIn, } return response.Success(c, authResponse, "Token refreshed successfully") } // Try panel user token refresh if userAuthResponse, err := h.userAuthService.RefreshToken(c.Request().Context(), req.RefreshToken); err == nil { // Convert to generic auth response authResponse := &domain.AuthResponse{ User: userAuthResponse.User, AccessToken: userAuthResponse.AccessToken, RefreshToken: userAuthResponse.RefreshToken, ExpiresIn: userAuthResponse.ExpiresIn, } return response.Success(c, authResponse, "Token refreshed successfully") } h.logger.Warn("Token refresh failed for both customer and user", map[string]interface{}{}) return response.Unauthorized(c, "Invalid refresh token") } // ChangePassword handles password change (determines user type from context) // @Summary Change user password // @Description Change authenticated user's password // @Tags auth // @Accept json // @Produce json // @Security ApiKeyAuth // @Param request body ChangePasswordRequest true "Change password request" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /auth/change-password [post] func (h *AuthHandler) ChangePassword(c echo.Context) error { var req struct { OldPassword string `json:"old_password" validate:"required"` NewPassword string `json:"new_password" validate:"required,min=8"` } // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Check if it's a customer if customer, ok := c.Get("customer").(*domain.Customer); ok { err := h.customerAuthService.ChangePassword(c.Request().Context(), customer.ID, req.OldPassword, req.NewPassword) if err != nil { h.logger.Error("Customer password change failed", map[string]interface{}{ "error": err.Error(), "customer_id": customer.ID.String(), }) if err.Error() == "invalid old password" { return response.BadRequest(c, "Invalid old password", "") } return response.InternalServerError(c, "Password change failed") } return response.Success(c, nil, "Password changed successfully") } // Check if it's a panel user if user, ok := c.Get("user").(*domain.User); ok { err := h.userAuthService.ChangePassword(c.Request().Context(), user.ID, req.OldPassword, req.NewPassword) if err != nil { h.logger.Error("Panel user password change failed", map[string]interface{}{ "error": err.Error(), "user_id": user.ID.String(), }) if err.Error() == "invalid old password" { return response.BadRequest(c, "Invalid old password", "") } return response.InternalServerError(c, "Password change failed") } return response.Success(c, nil, "Password changed successfully") } return response.Unauthorized(c, "Authentication required") } // GetProfile returns current user profile (determines user type from context) // @Summary Get user profile // @Description Get authenticated user's profile information // @Tags auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse{data=domain.User} // @Failure 401 {object} response.APIResponse // @Router /auth/profile [get] func (h *AuthHandler) GetProfile(c echo.Context) error { // Check if it's a customer if customer, ok := c.Get("customer").(*domain.Customer); ok { return response.Success(c, customer, "Profile retrieved successfully") } // Check if it's a panel user if user, ok := c.Get("user").(*domain.User); ok { return response.Success(c, user, "Profile retrieved successfully") } return response.Unauthorized(c, "Authentication required") } // Logout handles user logout (determines user type from context) // @Summary User logout // @Description Logout user (client should remove tokens) // @Tags auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse // @Router /auth/logout [post] func (h *AuthHandler) Logout(c echo.Context) error { // Check if it's a customer if customer, ok := c.Get("customer").(*domain.Customer); ok { h.logger.Info("Customer logged out", map[string]interface{}{ "customer_id": customer.ID.String(), "email": customer.Email, }) return response.Success(c, nil, "Logged out successfully") } // Check if it's a panel user if user, ok := c.Get("user").(*domain.User); ok { h.logger.Info("Panel user logged out", map[string]interface{}{ "user_id": user.ID.String(), "email": user.Email, }) return response.Success(c, nil, "Logged out successfully") } return response.Success(c, nil, "Logged out successfully") }