Enhance API documentation and restructure routes for improved clarity
- Updated README.md to include comprehensive Swagger documentation details, highlighting new features and endpoint categories. - Introduced a new router structure to streamline route registration for admin and public endpoints, enhancing maintainability. - Updated health check endpoint documentation to reflect comprehensive server status information. - Enhanced customer and company management routes with improved descriptions and examples in Swagger. - Refactored customer and user handlers to remove obsolete route registrations, aligning with the new router structure. - Improved response handling in customer and user services to utilize string IDs consistently. - Updated validation rules and forms for customer management to support multiple company assignments. - Enhanced logging practices across services to ensure better traceability and error handling.
This commit is contained in:
+181
-35
@@ -28,38 +28,6 @@ func NewHandler(service Service, userHandler *user.Handler, authService authoriz
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers customer routes
|
||||
func (h *Handler) RegisterRoutes(e *echo.Echo) {
|
||||
|
||||
adminV1 := e.Group("/admin/v1/customers")
|
||||
adminV1.Use(h.userHandler.AuthMiddleware())
|
||||
adminV1.POST("", h.CreateCustomer)
|
||||
adminV1.GET("", h.ListCustomers)
|
||||
adminV1.GET("/:id", h.GetCustomerByID)
|
||||
adminV1.PUT("/:id", h.UpdateCustomer)
|
||||
adminV1.DELETE("/:id", h.DeleteCustomer)
|
||||
adminV1.PATCH("/:id/status", h.UpdateCustomerStatus)
|
||||
adminV1.PATCH("/:id/verification", h.UpdateCustomerVerification)
|
||||
adminV1.POST("/:id/verify", h.VerifyCustomer)
|
||||
adminV1.POST("/:id/suspend", h.SuspendCustomer)
|
||||
adminV1.POST("/:id/activate", h.ActivateCustomer)
|
||||
adminV1.GET("/company/:companyId", h.GetCustomersByCompanyID)
|
||||
adminV1.GET("/type/:type", h.GetCustomersByType)
|
||||
adminV1.GET("/status/:status", h.GetCustomersByStatus)
|
||||
|
||||
// Mobile-specific endpoints
|
||||
v1 := e.Group("/api/v1")
|
||||
authorizationGP := v1.Group("")
|
||||
authorizationGP.POST("/login", h.Login)
|
||||
authorizationGP.POST("/refresh-token", h.RefreshToken)
|
||||
|
||||
profileGP := v1.Group("")
|
||||
profileGP.Use(h.AuthMiddleware())
|
||||
profileGP.GET("/profile", h.GetProfile)
|
||||
profileGP.DELETE("/logout", h.Logout)
|
||||
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -127,6 +95,33 @@ func (h *Handler) GetCustomerByID(c echo.Context) error {
|
||||
return response.Success(c, customer.ToResponse(), "Customer retrieved successfully")
|
||||
}
|
||||
|
||||
// GetCustomerByIDWithCompanies retrieves a customer by ID with companies (Web Panel)
|
||||
// @Summary Get customer by ID with companies
|
||||
// @Description Retrieve detailed customer information by customer ID including assigned companies
|
||||
// @Tags Customers-Admin
|
||||
// @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}/with-companies [get]
|
||||
func (h *Handler) GetCustomerByIDWithCompanies(c echo.Context) error {
|
||||
idStr := c.Param("id")
|
||||
|
||||
customer, err := h.service.GetCustomerByIDWithCompanies(c.Request().Context(), idStr)
|
||||
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
|
||||
@@ -247,6 +242,45 @@ func (h *Handler) ListCustomers(c echo.Context) error {
|
||||
return response.Success(c, result, "Customers retrieved successfully")
|
||||
}
|
||||
|
||||
// ListCustomersWithCompanies lists customers with companies and filters
|
||||
// @Summary List customers with companies and filters
|
||||
// @Description Retrieve a paginated list of customers with their assigned companies and advanced filtering options including search, type, status, company, industry, verification status, and compliance status. Supports sorting and pagination.
|
||||
// @Tags Customers-Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param search query string false "Search term to filter customers by name, email, or company name"
|
||||
// @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, pending)
|
||||
// @Param company_id query string false "Filter by company ID" format(uuid)
|
||||
// @Param industry query string false "Filter by industry"
|
||||
// @Param is_verified query boolean false "Filter by verification status"
|
||||
// @Param is_compliant query boolean false "Filter by compliance status"
|
||||
// @Param language query string false "Filter by preferred language"
|
||||
// @Param currency query string false "Filter by preferred currency"
|
||||
// @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 with companies 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/with-companies [get]
|
||||
func (h *Handler) ListCustomersWithCompanies(c echo.Context) error {
|
||||
form, err := response.Parse[ListCustomersForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
result, err := h.service.ListCustomersWithCompanies(c.Request().Context(), form)
|
||||
if err != nil {
|
||||
return response.InternalServerError(c, "Failed to list customers with companies")
|
||||
}
|
||||
|
||||
return response.Success(c, result, "Customers with companies retrieved successfully")
|
||||
}
|
||||
|
||||
// UpdateCustomerStatus updates customer status (Web Panel)
|
||||
// @Summary Update customer status
|
||||
// @Description Update customer account status (active, inactive, suspended, pending)
|
||||
@@ -604,6 +638,88 @@ func (h *Handler) GetCustomersByStatus(c echo.Context) error {
|
||||
return response.SuccessWithMeta(c, customerResponses, meta, "Customers retrieved successfully")
|
||||
}
|
||||
|
||||
// AssignCompaniesToCustomer assigns companies to a customer (Web Panel)
|
||||
// @Summary Assign companies to a customer
|
||||
// @Description Assign one or more companies to a specific customer.
|
||||
// @Tags Customers-Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Customer ID"
|
||||
// @Param companies body AssignCompaniesForm true "List of company IDs to assign"
|
||||
// @Success 200 {object} response.APIResponse "Companies 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}/companies/assign [post]
|
||||
func (h *Handler) AssignCompaniesToCustomer(c echo.Context) error {
|
||||
idStr := c.Param("id")
|
||||
form, err := response.Parse[AssignCompaniesForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
// Get user ID from JWT token context
|
||||
assignedBy, err := user.GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
return response.Unauthorized(c, "User not authenticated")
|
||||
}
|
||||
|
||||
err = h.service.AssignCompaniesToCustomer(c.Request().Context(), idStr, form.CompanyIDs, &assignedBy)
|
||||
if err != nil {
|
||||
if err.Error() == "customer not found" {
|
||||
return response.NotFound(c, "Customer not found")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to assign companies to customer")
|
||||
}
|
||||
|
||||
return response.Success(c, map[string]interface{}{
|
||||
"message": "Companies assigned successfully",
|
||||
}, "Companies assigned successfully")
|
||||
}
|
||||
|
||||
// RemoveCompaniesFromCustomer removes companies from a customer (Web Panel)
|
||||
// @Summary Remove companies from a customer
|
||||
// @Description Remove one or more companies from a specific customer.
|
||||
// @Tags Customers-Admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Customer ID"
|
||||
// @Param companies body RemoveCompaniesForm true "List of company IDs to remove"
|
||||
// @Success 200 {object} response.APIResponse "Companies removed 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}/companies/remove [post]
|
||||
func (h *Handler) RemoveCompaniesFromCustomer(c echo.Context) error {
|
||||
idStr := c.Param("id")
|
||||
form, err := response.Parse[RemoveCompaniesForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
// Get user ID from JWT token context
|
||||
removedBy, err := user.GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
return response.Unauthorized(c, "User not authenticated")
|
||||
}
|
||||
|
||||
err = h.service.RemoveCompaniesFromCustomer(c.Request().Context(), idStr, form.CompanyIDs, &removedBy)
|
||||
if err != nil {
|
||||
if err.Error() == "customer not found" {
|
||||
return response.NotFound(c, "Customer not found")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to remove companies from customer")
|
||||
}
|
||||
|
||||
return response.Success(c, map[string]interface{}{
|
||||
"message": "Companies removed successfully",
|
||||
}, "Companies removed successfully")
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -616,7 +732,7 @@ func (h *Handler) GetCustomersByStatus(c echo.Context) error {
|
||||
// @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/login [post]
|
||||
// @Router /api/v1/profile/login [post]
|
||||
func (h *Handler) Login(c echo.Context) error {
|
||||
form, err := response.Parse[LoginForm](c)
|
||||
if err != nil {
|
||||
@@ -650,7 +766,7 @@ func (h *Handler) Login(c echo.Context) error {
|
||||
// @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/refresh-token [post]
|
||||
// @Router /api/v1/profile/refresh-token [post]
|
||||
func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
form, err := response.Parse[RefreshTokenForm](c)
|
||||
if err != nil {
|
||||
@@ -680,7 +796,7 @@ func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
// @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]
|
||||
// @Router /api/v1/ [get]
|
||||
func (h *Handler) GetProfile(c echo.Context) error {
|
||||
// Extract customer ID from JWT token context
|
||||
customerID, err := GetCustomerIDFromContext(c)
|
||||
@@ -699,6 +815,36 @@ func (h *Handler) GetProfile(c echo.Context) error {
|
||||
return response.Success(c, customer.ToResponse(), "Profile retrieved successfully")
|
||||
}
|
||||
|
||||
// GetProfileWithCompanies retrieves customer profile with companies (Mobile)
|
||||
// @Summary Get customer profile with companies
|
||||
// @Description Retrieve current customer profile information along with their assigned companies.
|
||||
// @Tags Customers-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/with-companies [get]
|
||||
func (h *Handler) GetProfileWithCompanies(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")
|
||||
}
|
||||
|
||||
customer, 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 with companies")
|
||||
}
|
||||
|
||||
return response.Success(c, customer, "Profile retrieved successfully")
|
||||
}
|
||||
|
||||
// Logout handles customer logout (Mobile)
|
||||
// @Summary Customer logout
|
||||
// @Description Logout customer and invalidate access token
|
||||
|
||||
Reference in New Issue
Block a user