package customer import ( "net/http" "tm/pkg/response" "github.com/asaskevich/govalidator" "github.com/google/uuid" "github.com/labstack/echo/v4" ) // Handler handles HTTP requests for customer operations type Handler struct { service Service } // NewHandler creates a new customer handler func NewHandler(service Service) *Handler { return &Handler{ service: service, } } // RegisterRoutes registers all customer routes func (h *Handler) RegisterRoutes(e *echo.Echo) { v1 := e.Group("/api/v1") v1.POST("/customers/register", h.RegisterCustomer) // Public routes (no authentication required) v1.POST("/customers/login", h.Login) v1.POST("/customers/refresh-token", h.RefreshToken) // Protected routes (authentication required) customers := v1.Group("/customers") customers.Use(h.authMiddleware) // TODO: Implement auth middleware customers.GET("/profile", h.GetProfile) customers.PUT("/profile", h.UpdateProfile) customers.PUT("/change-password", h.ChangePassword) customers.POST("/device-token", h.AddDeviceToken) customers.DELETE("/device-token", h.RemoveDeviceToken) customers.POST("/logout", h.Logout) // Admin routes (admin authentication required) admin := v1.Group("/admin/customers") admin.Use(h.adminAuthMiddleware) // TODO: Implement admin auth middleware admin.POST("/register", h.RegisterCustomer) admin.GET("", h.ListCustomers) admin.GET("/:id", h.GetCustomerByID) admin.PUT("/:id/status", h.UpdateCustomerStatus) admin.GET("/device-tokens", h.GetAllDeviceTokens) } // RegisterCustomer handles customer registration // @Summary Register a new customer // @Description Register a new customer with the provided information // @Tags customers // @Accept json // @Produce json // @Param customer body RegisterCustomerForm true "Customer registration information" // @Success 201 {object} response.APIResponse{data=customer.CustomerResponse} "Customer registered successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 409 {object} response.APIResponse "Customer already exists" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /admin/customers/register [post] func (h *Handler) RegisterCustomer(c echo.Context) error { var form RegisterCustomerForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Register customer customer, err := h.service.RegisterCustomer(c.Request().Context(), &form) if err != nil { return response.Conflict(c, err.Error()) } return response.Created(c, customer.ToResponse(), "Customer registered successfully") } // Login handles customer authentication // @Summary Customer login // @Description Authenticate customer with email and password // @Tags customers // @Accept json // @Produce json // @Param credentials body LoginForm true "Login credentials" // @Success 200 {object} response.APIResponse{data=customer.AuthResponse} "Login successful" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Invalid credentials" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/login [post] func (h *Handler) Login(c echo.Context) error { var form LoginForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Authenticate customer authResponse, err := h.service.Login(c.Request().Context(), &form) if err != nil { return response.Unauthorized(c, err.Error()) } return response.Success(c, authResponse, "Login successful") } // RefreshToken handles token refresh // @Summary Refresh access token // @Description Refresh the access token using a refresh token // @Tags customers // @Accept json // @Produce json // @Param token body RefreshTokenForm true "Refresh token information" // @Success 200 {object} response.APIResponse{data=customer.AuthResponse} "Token refreshed successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Invalid refresh token" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/refresh-token [post] func (h *Handler) RefreshToken(c echo.Context) error { var form RefreshTokenForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Refresh token authResponse, err := h.service.RefreshToken(c.Request().Context(), form.RefreshToken) if err != nil { return response.Unauthorized(c, err.Error()) } return response.Success(c, authResponse, "Token refreshed successfully") } // GetProfile retrieves customer profile // @Summary Get customer profile // @Description Retrieve the authenticated customer's profile information // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.APIResponse{data=customer.CustomerResponse} "Profile retrieved successfully" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 404 {object} response.APIResponse "Customer not found" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/profile [get] func (h *Handler) GetProfile(c echo.Context) error { // Get customer ID from context (set by auth middleware) customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } // Get customer customer, err := h.service.GetCustomerByID(c.Request().Context(), customerID) if err != nil { return response.NotFound(c, "Customer not found") } return response.Success(c, customer.ToResponse(), "Profile retrieved successfully") } // UpdateProfile updates customer profile // @Summary Update customer profile // @Description Update the authenticated customer's profile information // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param profile body UpdateCustomerForm true "Profile update information" // @Success 200 {object} response.APIResponse{data=customer.CustomerResponse} "Profile updated successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/profile [put] func (h *Handler) UpdateProfile(c echo.Context) error { // Get customer ID from context customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } var form UpdateCustomerForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Update customer customer, err := h.service.UpdateCustomer(c.Request().Context(), customerID, &form) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, customer.ToResponse(), "Profile updated successfully") } // ChangePassword changes customer password // @Summary Change customer password // @Description Change the authenticated customer's password // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param password body ChangePasswordForm true "Password change information" // @Success 200 {object} response.APIResponse "Password changed successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/change-password [put] func (h *Handler) ChangePassword(c echo.Context) error { // Get customer ID from context customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } var form ChangePasswordForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Change password err = h.service.ChangePassword(c.Request().Context(), customerID, &form) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, nil, "Password changed successfully") } // AddDeviceToken adds a device token for push notifications // @Summary Add device token // @Description Add a device token for push notifications // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param device_token body AddDeviceTokenForm true "Device token information" // @Success 200 {object} response.APIResponse "Device token added successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/device-token [post] func (h *Handler) AddDeviceToken(c echo.Context) error { // Get customer ID from context customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } var form AddDeviceTokenForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Add device token err = h.service.AddDeviceToken(c.Request().Context(), customerID, &form) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, nil, "Device token added successfully") } // RemoveDeviceToken removes a device token // @Summary Remove device token // @Description Remove a device token for push notifications // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param device_token body RemoveDeviceTokenForm true "Device token information" // @Success 200 {object} response.APIResponse "Device token removed successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/device-token [delete] func (h *Handler) RemoveDeviceToken(c echo.Context) error { // Get customer ID from context customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } var form RemoveDeviceTokenForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Remove device token err = h.service.RemoveDeviceToken(c.Request().Context(), customerID, &form) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, nil, "Device token removed successfully") } // Logout handles customer logout // @Summary Customer logout // @Description Logout customer and invalidate device token // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param logout body object true "Logout request with device token" // @Success 200 {object} response.APIResponse "Logged out successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /customers/logout [post] func (h *Handler) Logout(c echo.Context) error { // Get customer ID from context customerID, err := h.getCustomerIDFromContext(c) if err != nil { return response.Unauthorized(c, "Invalid authentication") } // Get device token from request body var req struct { DeviceToken string `json:"device_token" valid:"required"` } if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate request if _, err := govalidator.ValidateStruct(req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Logout err = h.service.Logout(c.Request().Context(), customerID, req.DeviceToken) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, nil, "Logged out successfully") } // ListCustomers lists customers (admin only) // @Summary List customers // @Description Retrieve a paginated list of customers (admin only) // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param page query int false "Page number" default(1) // @Param limit query int false "Number of items per page" default(20) // @Param search query string false "Search term" // @Success 200 {object} response.APIResponse{data=[]customer.CustomerResponse} "Customers retrieved successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /admin/customers [get] func (h *Handler) ListCustomers(c echo.Context) error { var form ListCustomersForm // Bind query parameters if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid query parameters", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // List customers customers, total, err := h.service.ListCustomers(c.Request().Context(), &form) if err != nil { return response.InternalServerError(c, err.Error()) } // Convert to responses var responses []*CustomerResponse for _, customer := range customers { responses = append(responses, customer.ToResponse()) } // Create metadata meta := &response.Meta{ Total: total, Limit: 20, // Default limit Offset: 0, // Default offset } if form.Limit != nil { meta.Limit = *form.Limit } if form.Offset != nil { meta.Offset = *form.Offset } return response.SuccessWithMeta(c, responses, meta, "Customers retrieved successfully") } // GetCustomerByID retrieves a customer by ID (admin only) // @Summary Get customer by ID // @Description Retrieve customer information by ID (admin only) // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Customer ID" // @Success 200 {object} response.APIResponse{data=customer.CustomerResponse} "Customer retrieved successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 404 {object} response.APIResponse "Customer not found" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /admin/customers/{id} [get] func (h *Handler) GetCustomerByID(c echo.Context) error { // Parse customer ID customerIDStr := c.Param("id") customerID, err := uuid.Parse(customerIDStr) if err != nil { return response.BadRequest(c, "Invalid customer ID", err.Error()) } // Get customer customer, err := h.service.GetCustomerByID(c.Request().Context(), customerID) if err != nil { return response.NotFound(c, "Customer not found") } return response.Success(c, customer.ToResponse(), "Customer retrieved successfully") } // UpdateCustomerStatus updates customer status (admin only) // @Summary Update customer status // @Description Update customer status (admin only) // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Param id path string true "Customer ID" // @Param status body UpdateCustomerStatusForm true "Status update information" // @Success 200 {object} response.APIResponse "Customer status updated successfully" // @Failure 400 {object} response.APIResponse "Bad request" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 404 {object} response.APIResponse "Customer not found" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /admin/customers/{id}/status [put] func (h *Handler) UpdateCustomerStatus(c echo.Context) error { // Parse customer ID customerIDStr := c.Param("id") customerID, err := uuid.Parse(customerIDStr) if err != nil { return response.BadRequest(c, "Invalid customer ID", err.Error()) } var form UpdateCustomerStatusForm if err := c.Bind(&form); err != nil { return response.BadRequest(c, "Invalid request body", err.Error()) } // Validate form if _, err := govalidator.ValidateStruct(form); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Update status err = h.service.UpdateCustomerStatus(c.Request().Context(), customerID, &form) if err != nil { return response.BadRequest(c, err.Error(), "") } return response.Success(c, nil, "Customer status updated successfully") } // GetAllDeviceTokens retrieves all device tokens (admin only) // @Summary Get all device tokens // @Description Retrieve all device tokens (admin only) // @Tags customers // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.APIResponse{data=[]string} "Device tokens retrieved successfully" // @Failure 401 {object} response.APIResponse "Unauthorized" // @Failure 500 {object} response.APIResponse "Internal server error" // @Router /admin/customers/device-tokens [get] func (h *Handler) GetAllDeviceTokens(c echo.Context) error { // Get all device tokens tokens, err := h.service.GetAllDeviceTokens(c.Request().Context()) if err != nil { return response.InternalServerError(c, err.Error()) } return response.Success(c, tokens, "Device tokens retrieved successfully") } // Helper methods // getCustomerIDFromContext extracts customer ID from context func (h *Handler) getCustomerIDFromContext(c echo.Context) (uuid.UUID, error) { // TODO: Implement proper JWT token validation and extraction // For now, we'll use a header-based approach customerIDStr := c.Request().Header.Get("X-Customer-ID") if customerIDStr == "" { return uuid.Nil, echo.NewHTTPError(http.StatusUnauthorized, "Missing customer ID") } customerID, err := uuid.Parse(customerIDStr) if err != nil { return uuid.Nil, echo.NewHTTPError(http.StatusUnauthorized, "Invalid customer ID") } return customerID, nil } // authMiddleware is a placeholder for authentication middleware func (h *Handler) authMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // TODO: Implement proper JWT token validation // For now, we'll just pass through return next(c) } } // adminAuthMiddleware is a placeholder for admin authentication middleware func (h *Handler) adminAuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // TODO: Implement proper admin authentication // For now, we'll just pass through return next(c) } }