second commit
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user