package v1 import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" "github.com/gochat/gochat/pkg/response" ) // PlatformAccountUserHandler handles Platform API account_user endpoints (AccessToken auth). // Reference: Chatwoot Platform::Api::V1::AccountUsersController — AccessToken authenticated // // Manages the association between users and accounts from the Platform API context. // Both the account and the user must be within the PlatformApp's permissible scope. type PlatformAccountUserHandler struct { accountRepo *repository.AccountRepo userRepo *repository.UserRepo permissibleRepo *repository.PermissibleRepo } // NewPlatformAccountUserHandler creates a new PlatformAccountUser handler. func NewPlatformAccountUserHandler( accountRepo *repository.AccountRepo, userRepo *repository.UserRepo, permissibleRepo *repository.PermissibleRepo, ) *PlatformAccountUserHandler { return &PlatformAccountUserHandler{ accountRepo: accountRepo, userRepo: userRepo, permissibleRepo: permissibleRepo, } } // Index lists account_users for a specific account. // GET /platform/api/v1/accounts/:account_id/account_users // Reference: Chatwoot Platform::Api::V1::AccountUsersController#index // Requires: Account must be within PlatformApp's permissible scope. func (h *PlatformAccountUserHandler) Index(c *gin.Context) { accountID, err := parseUintAnyParam(c, "account_id", "id") if err != nil || accountID == 0 { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account ID") return } platformAppID := getPlatformAppID(c) // Verify PlatformApp has permissible access to this account perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAccount, accountID) if err != nil || perm == nil { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource") return } accountUsers, _, err := h.accountRepo.FindAgentsByAccount(c.Request.Context(), accountID, 0, 100) if err != nil { response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) return } c.JSON(http.StatusOK, accountUsers) } // Create adds a user to an account. // POST /platform/api/v1/accounts/:account_id/account_users // Reference: Chatwoot Platform::Api::V1::AccountUsersController#create // Requires: Both account and user must be within PlatformApp's permissible scope. func (h *PlatformAccountUserHandler) Create(c *gin.Context) { accountID, err := parseUintAnyParam(c, "account_id", "id") if err != nil || accountID == 0 { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account ID") return } platformAppID := getPlatformAppID(c) // Verify PlatformApp has permissible access to this account perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAccount, accountID) if err != nil || perm == nil { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource") return } var req struct { UserID uint `json:"user_id" form:"user_id" binding:"required"` Role *string `json:"role,omitempty" form:"role"` } if err := c.ShouldBind(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, err.Error()) return } acctUser, err := h.accountRepo.UpsertAccountUser(c.Request.Context(), accountID, req.UserID, req.Role) if err != nil { response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) return } c.JSON(http.StatusOK, acctUser) } // Destroy removes a user from an account. // DELETE /platform/api/v1/accounts/:account_id/account_users/:id // Reference: Chatwoot Platform::Api::V1::AccountUsersController#destroy // Requires: Account must be within PlatformApp's permissible scope. func (h *PlatformAccountUserHandler) Destroy(c *gin.Context) { accountID, err := parseUintAnyParam(c, "account_id", "id") if err != nil || accountID == 0 { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account ID") return } userID, err := platformAccountUserID(c) if err != nil || userID == 0 { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid user ID") return } platformAppID := getPlatformAppID(c) // Verify PlatformApp has permissible access to this account perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAccount, accountID) if err != nil || perm == nil { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource") return } if err := h.accountRepo.RemoveUserFromAccount(c.Request.Context(), accountID, userID); err != nil { response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error()) return } c.Status(http.StatusOK) } func platformAccountUserID(c *gin.Context) (uint, error) { if raw := c.Param("user_id"); raw != "" { id, err := strconv.ParseUint(raw, 10, 32) return uint(id), err } var req struct { UserID uint `json:"user_id" form:"user_id"` } if err := c.ShouldBind(&req); err != nil { return 0, err } return req.UserID, nil }