package v1 import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/service" applogger "github.com/gochat/gochat/pkg/logger" "github.com/gochat/gochat/pkg/pagination" "github.com/gochat/gochat/pkg/response" ) // CaptainDocumentHandler handles CaptainDocument REST API endpoints. // Reference: Chatwoot enterprise/app/controllers/api/v1/captain/documents_controller.rb type CaptainDocumentHandler struct { svc *service.CaptainDocumentService } // NewCaptainDocumentHandler creates a new CaptainDocumentHandler. func NewCaptainDocumentHandler(svc *service.CaptainDocumentService) *CaptainDocumentHandler { return &CaptainDocumentHandler{svc: svc} } // Create creates a new captain document. // POST /api/v1/accounts/:account_id/captain_assistants/:assistant_id/documents func (h *CaptainDocumentHandler) Create(c *gin.Context) { accountID, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id") return } assistantID, err := strconv.ParseUint(c.Param("assistant_id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid assistant_id") return } var req service.CreateDocumentRequest if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } doc, err := h.svc.Create(c.Request.Context(), uint(assistantID), uint(accountID), &req) if err != nil { applogger.L().Errorf("Create captain document: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to create document") return } response.Created(c, doc) } // Get retrieves a captain document by ID. // GET /api/v1/accounts/:account_id/captain_documents/:id func (h *CaptainDocumentHandler) Get(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } doc, err := h.svc.Get(c.Request.Context(), uint(id)) if err != nil { applogger.L().Errorf("Get captain document: %v", err) response.AbortWithStatusError(c, http.StatusNotFound, response.ErrNotFound, "document not found") return } response.OK(c, doc) } // Update updates an existing captain document. // PUT /api/v1/accounts/:account_id/captain_documents/:id func (h *CaptainDocumentHandler) Update(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } var req service.UpdateDocumentRequest if err := c.ShouldBindJSON(&req); err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error()) return } doc, err := h.svc.Update(c.Request.Context(), uint(id), &req) if err != nil { applogger.L().Errorf("Update captain document: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to update document") return } response.OK(c, doc) } // Delete deletes a captain document. // DELETE /api/v1/accounts/:account_id/captain_documents/:id func (h *CaptainDocumentHandler) Delete(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if err := h.svc.Delete(c.Request.Context(), uint(id)); err != nil { applogger.L().Errorf("Delete captain document: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to delete document") return } response.NoContent(c) } // List retrieves documents for an assistant. // GET /api/v1/accounts/:account_id/captain_assistants/:assistant_id/documents func (h *CaptainDocumentHandler) List(c *gin.Context) { assistantID, err := strconv.ParseUint(c.Param("assistant_id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid assistant_id") return } p := pagination.Parse(c) docs, count, err := h.svc.List(c.Request.Context(), uint(assistantID), p.Offset, p.PerPage) if err != nil { applogger.L().Errorf("List captain documents: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to list documents") return } response.OKWithMeta(c, docs, p.Page, p.PerPage, count) } // ProcessDocument triggers document content extraction and embedding generation. // POST /api/v1/accounts/:account_id/captain_documents/:id/process func (h *CaptainDocumentHandler) ProcessDocument(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if err := h.svc.ProcessDocument(c.Request.Context(), uint(id)); err != nil { applogger.L().Errorf("ProcessDocument: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to process document") return } response.OK(c, gin.H{"processed": true}) } // SyncDocument triggers re-fetching content from the external URL. // POST /api/v1/accounts/:account_id/captain_documents/:id/sync func (h *CaptainDocumentHandler) SyncDocument(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id") return } if err := h.svc.SyncDocument(c.Request.Context(), uint(id)); err != nil { applogger.L().Errorf("SyncDocument: %v", err) response.AbortWithStatusError(c, http.StatusInternalServerError, response.ErrInternal, "failed to sync document") return } response.OK(c, gin.H{"synced": true}) }