0da0c8b8c9
Added functionality to check for existing customers and users by phone number during registration and updates. Updated the handler, service, and repository layers to include phone number checks, ensuring unique phone numbers across customers and users.
620 lines
26 KiB
Go
620 lines
26 KiB
Go
package customer
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"tm/internal/user"
|
|
"tm/pkg/authorization"
|
|
"tm/pkg/logger"
|
|
"tm/pkg/response"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// Handler handles HTTP requests for customer operations
|
|
type Handler struct {
|
|
service Service
|
|
authService authorization.AuthorizationService
|
|
userHandler *user.Handler
|
|
logger logger.Logger
|
|
validator ValidationService
|
|
}
|
|
|
|
// NewHandler creates a new customer handler
|
|
func NewHandler(service Service, userHandler *user.Handler, authService authorization.AuthorizationService, logger logger.Logger, validator ValidationService) *Handler {
|
|
return &Handler{
|
|
service: service,
|
|
authService: authService,
|
|
userHandler: userHandler,
|
|
logger: logger,
|
|
validator: validator,
|
|
}
|
|
}
|
|
|
|
// CreateCustomer creates a new customer (Web Panel)
|
|
// @Summary Create a new customer
|
|
// @Description Create a new customer with comprehensive information including personal details, company information, address, and business details. This endpoint is used by the web panel for full customer registration.
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param customer body CreateCustomerForm true "Customer information including type (individual|company|government), personal details, company info, address, and business details"
|
|
// @Success 201 {object} response.APIResponse{data=CustomerResponse} "Customer created successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 409 {object} response.APIResponse "Conflict - Customer with this email/company already exists"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers [post]
|
|
func (h *Handler) CreateCustomer(c echo.Context) error {
|
|
form, err := response.Parse[CreateCustomerForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
customer, err := h.service.Register(c.Request().Context(), form)
|
|
if err != nil {
|
|
if err.Error() == "customer with this email already exists" ||
|
|
err.Error() == "customer with this username already exists" ||
|
|
err.Error() == "customer with this phone number already exists" ||
|
|
err.Error() == "company with this name already exists" ||
|
|
err.Error() == "company with this registration number already exists" ||
|
|
err.Error() == "company with this tax ID already exists" {
|
|
return response.Conflict(c, err.Error())
|
|
}
|
|
if strings.HasPrefix(err.Error(), "invalid company ID") || err.Error() == "invalid username format" {
|
|
return response.BadRequest(c, err.Error(), "")
|
|
}
|
|
return response.InternalServerError(c, "Failed to create customer")
|
|
}
|
|
|
|
return response.Created(c, customer, "Customer created successfully")
|
|
}
|
|
|
|
// GetCustomerByID retrieves a customer by ID (Web Panel)
|
|
// @Summary Get customer by ID
|
|
// @Description Retrieve detailed customer information by customer ID
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer retrieved successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id} [get]
|
|
func (h *Handler) GetCustomerByID(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
customer, err := h.service.GetByID(c.Request().Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to get customer")
|
|
}
|
|
|
|
return response.Success(c, customer, "Customer retrieved successfully")
|
|
}
|
|
|
|
// UpdateCustomer updates a customer (Web Panel)
|
|
// @Summary Update customer information
|
|
// @Description Update customer information including personal details, company information, address, and business details
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Param customer body UpdateCustomerForm true "Updated customer information"
|
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer updated successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 409 {object} response.APIResponse "Conflict - Customer with this email/company already exists"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id} [put]
|
|
func (h *Handler) UpdateCustomer(c echo.Context) error {
|
|
id := c.Param("id")
|
|
form, err := response.Parse[UpdateCustomerForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Get user ID from JWT token context
|
|
|
|
customer, err := h.service.Update(c.Request().Context(), id, form)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
if err.Error() == "customer with this email already exists" ||
|
|
err.Error() == "customer with this username already exists" ||
|
|
err.Error() == "customer with this phone number already exists" ||
|
|
err.Error() == "company with this name already exists" ||
|
|
err.Error() == "company with this registration number already exists" ||
|
|
err.Error() == "company with this tax ID already exists" {
|
|
return response.Conflict(c, err.Error())
|
|
}
|
|
if strings.HasPrefix(err.Error(), "invalid company ID") {
|
|
return response.BadRequest(c, err.Error(), err.Error())
|
|
}
|
|
return response.InternalServerError(c, "Failed to update customer")
|
|
}
|
|
|
|
return response.Success(c, customer, "Customer updated successfully")
|
|
}
|
|
|
|
// DeleteCustomer deletes a customer (Web Panel)
|
|
// @Summary Delete customer
|
|
// @Description Soft delete a customer by setting status to inactive
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Success 200 {object} response.APIResponse "Customer deleted successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id} [delete]
|
|
func (h *Handler) DeleteCustomer(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
err := h.service.Delete(c.Request().Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to delete customer")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Customer deleted successfully",
|
|
}, "Customer deleted successfully")
|
|
}
|
|
|
|
// ListCustomers lists customers with filters and pagination
|
|
// @Summary List customers with filters and pagination
|
|
// @Description Retrieve a paginated list of customers with filtering by full name, email, company, type, status, and role. Supports sorting and pagination.
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param q query string false "Search term to filter customers by email or username"
|
|
// @Param full_name query string false "Filter by customer full name (partial match)"
|
|
// @Param email query string false "Filter by customer email (partial match)"
|
|
// @Param type query string false "Filter by customer type" Enums(individual, company, government)
|
|
// @Param status query string false "Filter by customer status" Enums(active, inactive, suspended)
|
|
// @Param role query string false "Filter by customer role" Enums(admin, analyst)
|
|
// @Param company_id query string false "Filter by assigned company ID"
|
|
// @Param limit query integer false "Number of customers per page (1-100)" minimum(1) maximum(100) default(20)
|
|
// @Param offset query integer false "Number of customers to skip for pagination" minimum(0) default(0)
|
|
// @Param sort_by query string false "Field to sort by" Enums(email, company_name, created_at, updated_at, status) default(created_at)
|
|
// @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc)
|
|
// @Success 200 {object} response.APIResponse{data=CustomerListResponse} "Customers retrieved successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid query parameters"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers [get]
|
|
func (h *Handler) Search(c echo.Context) error {
|
|
form, err := response.Parse[SearchCustomersForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
pagination, err := response.NewPagination(c)
|
|
if err != nil {
|
|
return response.PaginationBadRequest(c, err)
|
|
}
|
|
|
|
result, err := h.service.Search(c.Request().Context(), form, pagination)
|
|
if err != nil {
|
|
if response.IsListPaginationError(err) {
|
|
return response.PaginationBadRequest(c, err)
|
|
}
|
|
return response.InternalServerError(c, "Failed to list customers")
|
|
}
|
|
|
|
return response.SuccessWithMeta(c, result, result.Meta, "Customers retrieved successfully")
|
|
}
|
|
|
|
// UpdateCustomerStatus updates customer status (Web Panel)
|
|
// @Summary Update customer status
|
|
// @Description Update customer account status (active, inactive, suspended, pending)
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Param status body UpdateStatusForm true "New customer status"
|
|
// @Success 200 {object} response.APIResponse "Customer status updated successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id}/status [patch]
|
|
func (h *Handler) UpdateStatus(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
form, err := response.Parse[UpdateStatusForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
err = h.service.UpdateStatus(c.Request().Context(), id, form)
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to update customer status")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Customer status updated successfully",
|
|
}, "Customer status updated successfully")
|
|
}
|
|
|
|
// ResetCustomerPassword resets a customer's password (admin only).
|
|
// @Summary Reset customer password
|
|
// @Description Generate a new password for a customer. The plaintext is returned once for manual delivery.
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "Customer ID"
|
|
// @Success 200 {object} response.APIResponse{data=AdminResetPasswordResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 401 {object} response.APIResponse
|
|
// @Failure 403 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 409 {object} response.APIResponse
|
|
// @Failure 429 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/customers/{id}/reset-password [post]
|
|
func (h *Handler) ResetCustomerPassword(c echo.Context) error {
|
|
c.Response().Header().Set("Cache-Control", "no-store")
|
|
|
|
actorID, err := user.GetUserIDFromContext(c)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Unauthorized")
|
|
}
|
|
|
|
targetID := c.Param("id")
|
|
result, err := h.service.AdminResetPassword(c.Request().Context(), actorID, targetID)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, errInvalidCustomerID):
|
|
return response.BadRequest(c, "Invalid id", "")
|
|
case errors.Is(err, errCustomerNotFound):
|
|
return response.NotFound(c, "Customer not found")
|
|
case errors.Is(err, errCannotResetCustPassword):
|
|
return response.Conflict(c, "Cannot reset password for this user")
|
|
case errors.Is(err, errCustResetPasswordRate):
|
|
return response.TooManyRequests(c, "Too many password reset requests")
|
|
default:
|
|
return response.InternalServerError(c, "Failed to reset password")
|
|
}
|
|
}
|
|
|
|
return response.Success(c, result, "Password has been reset successfully")
|
|
}
|
|
|
|
// AssignRole assigns a role to a customer (Web Panel)
|
|
// @Summary Assign role to customer
|
|
// @Description Assign a role (admin or analyst) to a customer
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Param role body AssignRoleForm true "Role assignment information"
|
|
// @Success 200 {object} response.APIResponse{data=AssignRoleResponse} "Role assigned successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id}/role [patch]
|
|
func (h *Handler) AssignRole(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
form, err := response.Parse[AssignRoleForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
result, err := h.service.AssignRole(c.Request().Context(), id, form)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
if err.Error() == "invalid role. Must be 'admin' or 'analyst'" {
|
|
return response.BadRequest(c, err.Error(), "")
|
|
}
|
|
return response.InternalServerError(c, "Failed to assign role to customer")
|
|
}
|
|
|
|
return response.Success(c, result, "Role assigned successfully")
|
|
}
|
|
|
|
// AssignCompanies assigns companies to a customer
|
|
// @Summary Assign companies to a customer
|
|
// @Description Assign companies to a customer
|
|
// @Tags Admin-Customers
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Customer ID"
|
|
// @Param companies body AssignCompaniesForm true "Companies to assign"
|
|
// @Success 200 {object} response.APIResponse "Companies assigned successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Security BearerAuth
|
|
// @Router /admin/v1/customers/{id}/companies [patch]
|
|
func (h *Handler) AssignCompanies(c echo.Context) error {
|
|
id := c.Param("id")
|
|
form, err := response.Parse[AssignCompaniesForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
err = h.service.AssignCompanies(c.Request().Context(), id, form.Companies)
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to assign companies to customer")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Companies assigned successfully",
|
|
}, "Companies assigned successfully")
|
|
}
|
|
|
|
// **************************************************
|
|
// * PROFILE HANDLERS *
|
|
// **************************************************
|
|
|
|
// Login handles customer authentication
|
|
// @Summary Customer login
|
|
// @Description Authenticate customer with username (email) and password. Returns access token, refresh token, and customer information upon successful authentication.
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param login body LoginForm true "Login credentials (username/email and password)"
|
|
// @Success 200 {object} response.APIResponse{data=AuthResponse} "Login successful"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid credentials or inactive account"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/login [post]
|
|
func (h *Handler) Login(c echo.Context) error {
|
|
form, err := response.Parse[LoginForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Call service
|
|
authResponse, err := h.service.Login(c.Request().Context(), form)
|
|
if err != nil {
|
|
if err.Error() == "invalid credentials" {
|
|
return response.Unauthorized(c, err.Error())
|
|
}
|
|
if err.Error() == "account is not active" {
|
|
return response.Unauthorized(c, err.Error())
|
|
}
|
|
return response.InternalServerError(c, "Failed to authenticate customer")
|
|
}
|
|
|
|
return response.Success(c, authResponse, "Login successful")
|
|
}
|
|
|
|
// RefreshToken handles customer token refresh
|
|
// @Summary Refresh customer access token
|
|
// @Description Refresh access token using a valid refresh token. This allows customers to maintain their session without re-authentication.
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param refresh body RefreshTokenForm true "Refresh token for generating new access token"
|
|
// @Success 200 {object} response.APIResponse{data=AuthResponse} "Token refreshed successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid refresh token or feature not implemented"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired refresh token"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/refresh-token [post]
|
|
func (h *Handler) RefreshToken(c echo.Context) error {
|
|
form, err := response.Parse[RefreshTokenForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Call service
|
|
authResponse, err := h.service.RefreshToken(c.Request().Context(), form.RefreshToken)
|
|
if err != nil {
|
|
if err.Error() == "token refresh not implemented yet" {
|
|
return response.BadRequest(c, "Token refresh not implemented yet", "")
|
|
}
|
|
return response.InternalServerError(c, "Failed to refresh token")
|
|
}
|
|
|
|
return response.Success(c, authResponse, "Token refreshed successfully")
|
|
}
|
|
|
|
// GetProfile retrieves customer profile information (Mobile)
|
|
// @Summary Get customer profile
|
|
// @Description Retrieve current customer profile information
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Profile retrieved successfully"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile [get]
|
|
func (h *Handler) GetProfile(c echo.Context) error {
|
|
// Extract customer ID from JWT token context
|
|
customerID, err := GetCustomerIDFromContext(c)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Customer not authenticated")
|
|
}
|
|
|
|
resp, err := h.service.GetProfileWithCompanies(c.Request().Context(), customerID)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to get customer profile")
|
|
}
|
|
|
|
return response.Success(c, resp, "Profile retrieved successfully")
|
|
}
|
|
|
|
// UpdateProfileKeywords updates customer profile keywords (Mobile)
|
|
// @Summary Update customer profile keywords
|
|
// @Description Update profile keywords manually; optional async keyword refresh is reserved for the AI service.
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param request body UpdateProfileKeywordsForm true "Manual keywords"
|
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Profile keywords update started"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
|
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/keywords [patch]
|
|
func (h *Handler) UpdateProfileKeywords(c echo.Context) error {
|
|
customerID, err := GetCustomerIDFromContext(c)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Customer not authenticated")
|
|
}
|
|
|
|
form, err := response.Parse[UpdateProfileKeywordsForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
resp, err := h.service.UpdateProfileKeywords(c.Request().Context(), customerID, form.Keywords)
|
|
if err != nil {
|
|
if err.Error() == "customer not found" {
|
|
return response.NotFound(c, "Customer not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to update profile keywords")
|
|
}
|
|
|
|
return response.Success(c, resp, "Profile keywords update started")
|
|
}
|
|
|
|
// Logout handles customer logout (Mobile)
|
|
// @Summary Customer logout
|
|
// @Description Logout customer and invalidate access token
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {object} response.APIResponse "Logout successful"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/logout [delete]
|
|
func (h *Handler) Logout(c echo.Context) error {
|
|
// Extract customer ID from JWT token context
|
|
customerID, err := GetCustomerIDFromContext(c)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Customer not authenticated")
|
|
}
|
|
|
|
// Get access token from context
|
|
accessToken, err := GetAccessTokenFromContext(c)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Access token not found")
|
|
}
|
|
|
|
err = h.service.Logout(c.Request().Context(), customerID, accessToken)
|
|
if err != nil {
|
|
// Don't return error here as the customer should still be logged out
|
|
h.logger.Warn("Failed to invalidate access token during logout", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Logout successful",
|
|
}, "Logout successful")
|
|
}
|
|
|
|
// RequestResetPassword handles password reset request
|
|
// @Summary Request password reset
|
|
// @Description Send a password reset OTP code to the customer's email address
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body RequestResetPasswordForm true "Email address for password reset"
|
|
// @Success 200 {object} response.APIResponse{data=RequestResetPasswordResponse} "Password reset code sent successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/forgot-password [post]
|
|
func (h *Handler) RequestResetPassword(c echo.Context) error {
|
|
form, err := response.Parse[RequestResetPasswordForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
result, err := h.service.RequestResetPassword(c.Request().Context(), form)
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to process password reset request")
|
|
}
|
|
|
|
return response.Success(c, result, "Password reset request processed")
|
|
}
|
|
|
|
// VerifyOTP handles OTP verification for password reset
|
|
// @Summary Verify OTP code
|
|
// @Description Verify the OTP code received via email and get a reset token
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body VerifyOTPForm true "Email and OTP code for verification"
|
|
// @Success 200 {object} response.APIResponse{data=VerifyOTPResponse} "OTP verified successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired OTP"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/verify-otp [post]
|
|
func (h *Handler) VerifyOTP(c echo.Context) error {
|
|
form, err := response.Parse[VerifyOTPForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
result, err := h.service.VerifyOTP(c.Request().Context(), form)
|
|
if err != nil {
|
|
return response.Unauthorized(c, "Invalid or expired verification code")
|
|
}
|
|
|
|
return response.Success(c, result, "OTP verified successfully")
|
|
}
|
|
|
|
// ResetPassword handles password reset with token
|
|
// @Summary Reset password
|
|
// @Description Reset the customer's password using the reset token
|
|
// @Tags Authorization
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body ResetPasswordForm true "Email, reset token, and new password"
|
|
// @Success 200 {object} response.APIResponse{data=ResetPasswordResponse} "Password reset successfully"
|
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired reset token"
|
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
// @Router /api/v1/profile/reset-password [post]
|
|
func (h *Handler) ResetPassword(c echo.Context) error {
|
|
form, err := response.Parse[ResetPasswordForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
result, err := h.service.ResetPassword(c.Request().Context(), form)
|
|
if err != nil {
|
|
return response.Unauthorized(c, err.Error())
|
|
}
|
|
|
|
return response.Success(c, result, "Password reset successfully")
|
|
}
|