package v1 import ( "net/http" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/service" "github.com/gochat/gochat/pkg/pagination" "github.com/gochat/gochat/pkg/response" ) // PlatformUserHandler handles Platform API user endpoints (AccessToken auth). // Reference: Chatwoot Platform::Api::V1::UsersController — AccessToken authenticated // // Distinct from SuperAdmin-platform routes: uses api_access_token header // authentication + Permissible system for resource-level access control. type PlatformUserHandler struct { svc *service.PlatformUserService } // NewPlatformUserHandler creates a new PlatformUser handler with service injection. func NewPlatformUserHandler(svc *service.PlatformUserService) *PlatformUserHandler { return &PlatformUserHandler{svc: svc} } // Show retrieves a user by ID. // GET /platform/api/v1/users/:id // Reference: Chatwoot Platform::Api::V1::UsersController#show // Requires: Permissible verification (PlatformApp must have access to this user) func (h *PlatformUserHandler) Show(c *gin.Context) { userID, err := parseUintParam(c, "id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) user, err := h.svc.GetUser(c.Request.Context(), platformAppID, userID) if err != nil { handlePlatformError(c, err) return } response.OK(c, user) } // Create creates a new user and auto-creates Permissible record. // POST /platform/api/v1/users // Reference: Chatwoot Platform::Api::V1::UsersController#create // Auto-permissible: PlatformApp automatically gets access to the created user. func (h *PlatformUserHandler) Create(c *gin.Context) { platformAppID := getPlatformAppID(c) var req struct { Name string `json:"name" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, err.Error()) return } user, err := h.svc.CreateUser(c.Request.Context(), platformAppID, req.Name, req.Email, req.Password) if err != nil { response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) return } response.Created(c, user) } // Login generates an SSO login link for a user. // POST /platform/api/v1/users/:id/login // Reference: Chatwoot Platform::Api::V1::UsersController#login // Returns: { url: sso_redirect_url } func (h *PlatformUserHandler) Login(c *gin.Context) { userID, err := parseUintParam(c, "id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) // Verify Permissible access first if err := h.svc.ValidatePermissible(c.Request.Context(), platformAppID, userID); err != nil { handlePlatformError(c, err) return } // SSO link generation — requires full SSO implementation (HMAC/JWT token, user lookup). // Production note: When SSO middleware is wired, this endpoint will generate // a signed redirect URL based on the user record and SSO configuration. response.OK(c, gin.H{ "url": "", // Would be populated with SSO redirect URL "id": userID, }) } // Token returns the SSO authentication token for a user. // POST /platform/api/v1/users/:id/token // Reference: Chatwoot Platform::Api::V1::UsersController#token // Returns: user resource with sso_auth_token field func (h *PlatformUserHandler) Token(c *gin.Context) { userID, err := parseUintParam(c, "id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) // Verify Permissible access first if err := h.svc.ValidatePermissible(c.Request.Context(), platformAppID, userID); err != nil { handlePlatformError(c, err) return } user, err := h.svc.GetUser(c.Request.Context(), platformAppID, userID) if err != nil { handlePlatformError(c, err) return } response.OK(c, gin.H{ "id": user.ID, "sso_auth_token": "", // Would be populated with SSO token }) } // Update updates a user. // PATCH /platform/api/v1/users/:id // Reference: Chatwoot Platform::Api::V1::UsersController#update // Requires: Permissible verification func (h *PlatformUserHandler) Update(c *gin.Context) { userID, err := parseUintParam(c, "id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) var req struct { Name string `json:"name,omitempty"` Email string `json:"email,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, err.Error()) return } user, err := h.svc.UpdateUser(c.Request.Context(), platformAppID, userID, req.Name, req.Email) if err != nil { handlePlatformError(c, err) return } response.OK(c, user) } // Destroy deletes a user. // DELETE /platform/api/v1/users/:id // Reference: Chatwoot Platform::Api::V1::UsersController#destroy // Requires: Permissible verification func (h *PlatformUserHandler) Destroy(c *gin.Context) { userID, err := parseUintParam(c, "id") if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) if err := h.svc.DeleteUser(c.Request.Context(), platformAppID, userID); err != nil { handlePlatformError(c, err) return } response.NoContent(c) } // --- Helper functions for Platform API handlers --- // getPlatformAppID extracts platform_app_id from the Gin context. // Set by PlatformAppAuth middleware during AccessToken authentication. func getPlatformAppID(c *gin.Context) uint { if id, exists := c.Get("platform_app_id"); exists { switch v := id.(type) { case uint: return v case float64: return uint(v) case int: return uint(v) } } return 0 } // handlePlatformError maps service errors to HTTP responses. func handlePlatformError(c *gin.Context, err error) { if err.Error() == "non permissible resource" { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource") return } response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) } // PlatformUsersListHandler returns all users the PlatformApp has permissible access to. // GET /platform/api/v1/users // Reference: Chatwoot Platform::Api::V1::UsersController#index (lists permissibles) func (h *PlatformUserHandler) List(c *gin.Context) { platformAppID := getPlatformAppID(c) page := pagination.Parse(c) users, err := h.svc.ListPermissibleUsers(c.Request.Context(), platformAppID) if err != nil { response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) return } // Manual pagination since we filter by permissible first total := int64(len(users)) start := page.Offset if start > len(users) { start = len(users) } end := start + page.PerPage if end > len(users) { end = len(users) } response.OKWithMeta(c, users[start:end], page.Page, page.PerPage, total) }