Files
gochat/internal/handler/api/v1/platform_agent_bot_handler.go
T
2026-06-04 15:44:48 +08:00

249 lines
8.1 KiB
Go

package v1
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/pkg/response"
)
// PlatformAgentBotHandler handles Platform API agent bot endpoints (AccessToken auth).
// Reference: Chatwoot Platform::Api::V1::AgentBotsController — AccessToken authenticated
//
// Distinct from account-level AgentBotHandler: this uses AccessToken authentication
// + Permissible system for resource-level access control.
// Platform-level bots (account_id == nil) and account-scoped bots are both accessible.
type PlatformAgentBotHandler struct {
agentBotRepo *repository.AgentBotRepo
permissibleRepo *repository.PermissibleRepo
}
// NewPlatformAgentBotHandler creates a new PlatformAgentBot handler.
func NewPlatformAgentBotHandler(
agentBotRepo *repository.AgentBotRepo,
permissibleRepo *repository.PermissibleRepo,
) *PlatformAgentBotHandler {
return &PlatformAgentBotHandler{
agentBotRepo: agentBotRepo,
permissibleRepo: permissibleRepo,
}
}
// List returns all agent bots the PlatformApp has permissible access to.
// GET /platform/api/v1/agent_bots
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#index
func (h *PlatformAgentBotHandler) List(c *gin.Context) {
platformAppID := getPlatformAppID(c)
permissibles, err := h.permissibleRepo.FindByPlatformAppID(c.Request.Context(), platformAppID)
if err != nil {
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error())
return
}
var bots []model.AgentBot
for _, perm := range permissibles {
if perm.PermissibleType == model.PermissibleTypeAgentBot {
bot, err := h.agentBotRepo.FindByID(c.Request.Context(), perm.PermissibleID)
if err != nil {
continue
}
bots = append(bots, *bot)
}
}
response.OK(c, bots)
}
// Show retrieves an agent bot by ID.
// GET /platform/api/v1/agent_bots/:id
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#show
// Requires: Permissible verification
func (h *PlatformAgentBotHandler) Show(c *gin.Context) {
botID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid bot ID")
return
}
platformAppID := getPlatformAppID(c)
// Verify Permissible access
perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAgentBot, botID)
if err != nil || perm == nil {
response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource")
return
}
bot, err := h.agentBotRepo.FindByID(c.Request.Context(), botID)
if err != nil {
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "agent bot not found")
return
}
response.OK(c, bot)
}
// Create creates a new agent bot and auto-creates Permissible record.
// POST /platform/api/v1/agent_bots
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#create
// Auto-permissible: PlatformApp automatically gets access to the created bot.
func (h *PlatformAgentBotHandler) Create(c *gin.Context) {
platformAppID := getPlatformAppID(c)
var req struct {
Name string `json:"name" binding:"required"`
Description string `json:"description,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
AccountID *uint `json:"account_id,omitempty"`
Config model.AgentBot `json:"config,omitempty"` // Embedded struct for bot config
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, err.Error())
return
}
bot := &model.AgentBot{
AccountID: req.AccountID,
Name: req.Name,
Description: req.Description,
AvatarURL: req.AvatarURL,
}
if err := h.agentBotRepo.Create(c.Request.Context(), bot); err != nil {
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error())
return
}
// Auto-create Permissible
perm := &model.Permissible{
PlatformAppID: platformAppID,
PermissibleType: model.PermissibleTypeAgentBot,
PermissibleID: bot.ID,
}
if err := h.permissibleRepo.Create(c.Request.Context(), perm); err != nil {
// Non-critical
}
response.Created(c, bot)
}
// Update updates an agent bot.
// PUT /platform/api/v1/agent_bots/:id
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#update
// Requires: Permissible verification
func (h *PlatformAgentBotHandler) Update(c *gin.Context) {
botID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid bot ID")
return
}
platformAppID := getPlatformAppID(c)
// Verify Permissible access
perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAgentBot, botID)
if err != nil || perm == nil {
response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource")
return
}
bot, err := h.agentBotRepo.FindByID(c.Request.Context(), botID)
if err != nil {
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "agent bot not found")
return
}
var req struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, err.Error())
return
}
if req.Name != "" {
bot.Name = req.Name
}
if req.Description != "" {
bot.Description = req.Description
}
if req.AvatarURL != "" {
bot.AvatarURL = req.AvatarURL
}
if err := h.agentBotRepo.Update(c.Request.Context(), bot); err != nil {
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error())
return
}
response.OK(c, bot)
}
// Destroy deletes an agent bot.
// DELETE /platform/api/v1/agent_bots/:id
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#destroy
// Requires: Permissible verification
func (h *PlatformAgentBotHandler) Destroy(c *gin.Context) {
botID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid bot ID")
return
}
platformAppID := getPlatformAppID(c)
// Verify Permissible access
perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAgentBot, botID)
if err != nil || perm == nil {
response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource")
return
}
if err := h.agentBotRepo.Delete(c.Request.Context(), botID); err != nil {
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error())
return
}
response.NoContent(c)
}
// DeleteAvatar removes the avatar of an agent bot.
// POST /platform/api/v1/agent_bots/:id/delete_avatar
// Reference: Chatwoot Platform::Api::V1::AgentBotsController#delete_avatar
// Requires: Permissible verification
func (h *PlatformAgentBotHandler) DeleteAvatar(c *gin.Context) {
botID, err := parseUintParam(c, "id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid bot ID")
return
}
platformAppID := getPlatformAppID(c)
// Verify Permissible access
perm, err := h.permissibleRepo.FindByPlatformAppAndResource(c.Request.Context(), platformAppID, model.PermissibleTypeAgentBot, botID)
if err != nil || perm == nil {
response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "non permissible resource")
return
}
bot, err := h.agentBotRepo.FindByID(c.Request.Context(), botID)
if err != nil {
response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "agent bot not found")
return
}
bot.AvatarURL = ""
if err := h.agentBotRepo.Update(c.Request.Context(), bot); err != nil {
response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, err.Error())
return
}
response.OK(c, bot)
}