package v1 import ( service "tm/internal/customer/service" "tm/pkg/logger" "tm/pkg/response" "github.com/asaskevich/govalidator" "github.com/labstack/echo/v4" ) // CustomerHandler handles authentication related HTTP requests for mobile customers type CustomerHandler struct { service service.CustomerService logger logger.Logger } // NewCustomerHandler creates a new customer authentication handler func NewHandler( customerAuthService service.CustomerService, logger logger.Logger, ) *CustomerHandler { return &CustomerHandler{ service: customerAuthService, logger: logger, } } // Register handles customer registration // @Summary Register new customer // @Description Create a new customer account for mobile app // @Tags customer-auth // @Accept json // @Produce json // @Param request body domain.CustomerRegisterRequest true "Customer registration request" // @Success 201 {object} response.APIResponse{data=domain.CustomerAuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 409 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/mobile/auth/register [post] func (h *CustomerHandler) Register(c echo.Context) error { var req service.RegisterForm // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if _, err := govalidator.ValidateStruct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Call service authResponse, err := h.service.Register(c.Request().Context(), req) if err != nil { return response.InternalServerError(c, "Registration failed") } return response.Created(c, authResponse, "Customer registered successfully") } // Login handles customer authentication // @Summary Customer login // @Description Authenticate customer and return tokens // @Tags customer-auth // @Accept json // @Produce json // @Param request body domain.LoginRequest true "Login request" // @Success 200 {object} response.APIResponse{data=domain.CustomerAuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/mobile/auth/login [post] func (h *CustomerHandler) Login(c echo.Context) error { var req service.LoginForm // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if _, err := govalidator.ValidateStruct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Call service authResponse, err := h.service.Login(c.Request().Context(), req) if err != nil { return response.InternalServerError(c, "Login failed") } return response.Success(c, authResponse, "Login successful") } // RefreshToken handles token refresh for customers // @Summary Refresh customer access token // @Description Generate new access token using refresh token // @Tags customer-auth // @Accept json // @Produce json // @Param request body RefreshTokenRequest true "Refresh token request" // @Success 200 {object} response.APIResponse{data=domain.CustomerAuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/mobile/auth/refresh [post] func (h *CustomerHandler) RefreshToken(c echo.Context) error { var req service.RefreshTokenForm // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if _, err := govalidator.ValidateStruct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Call service authResponse, err := h.service.RefreshToken(c.Request().Context(), req.RefreshToken) if err != nil { return response.InternalServerError(c, "Token refresh failed") } return response.Success(c, authResponse, "Token refreshed successfully") } // ChangePassword handles password change for customers // @Summary Change customer password // @Description Change authenticated customer's password // @Tags customer-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 /api/mobile/auth/change-password [post] func (h *CustomerHandler) ChangePassword(c echo.Context) error { var req service.ChangePasswordForm // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if _, err := govalidator.ValidateStruct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Get customer from context (set by auth middleware) // customer, ok := c.Get("customer").(*domain.Customer) // if !ok { // return response.Unauthorized(c, "Authentication required") // } // Call service // 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") } // GetProfile returns current customer profile // @Summary Get customer profile // @Description Get authenticated customer's profile information // @Tags customer-auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse{data=domain.Customer} // @Failure 401 {object} response.APIResponse // @Router /api/mobile/auth/profile [get] func (h *CustomerHandler) GetProfile(c echo.Context) error { // Get customer from context (set by auth middleware) // customer, ok := c.Get("customer").(*domain.Customer) // if !ok { // return response.Unauthorized(c, "Authentication required") // } return response.Success(c, nil, "Profile retrieved successfully") } // Logout handles customer logout // @Summary Customer logout // @Description Logout customer (client should remove tokens) // @Tags customer-auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse // @Router /api/mobile/auth/logout [post] func (h *CustomerHandler) Logout(c echo.Context) error { // In a stateless JWT implementation, logout is typically handled client-side // by removing the tokens from storage. However, you could implement token // blacklisting here if needed. // customer, ok := c.Get("customer").(*domain.Customer) // if 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") }