feat[Go] implement api/v1/thumbnails API (#15416)

### What problem does this PR solve?

As title

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality
This commit is contained in:
Haruko386
2026-06-01 11:22:08 +08:00
committed by GitHub
parent da1ed6f0e7
commit 2d7044b57e
3 changed files with 100 additions and 2 deletions

View File

@@ -18,8 +18,11 @@ package handler
import (
"encoding/json"
"errors"
"fmt"
"mime"
"net/http"
"path/filepath"
"ragflow/internal/common"
"ragflow/internal/entity"
"strconv"
@@ -31,6 +34,8 @@ import (
"ragflow/internal/service"
)
var IMG_BASE64_PREFIX = "data:image/png;base64,"
// DocumentHandler document handler
type DocumentHandler struct {
documentService *service.DocumentService
@@ -120,6 +125,58 @@ func (h *DocumentHandler) GetDocumentByID(c *gin.Context) {
})
}
// GetThumbnail Get thumbnails for documents.
func (h *DocumentHandler) GetThumbnail(c *gin.Context) {
_, errorCode, errorMessage := GetUser(c)
if errorCode != common.CodeSuccess {
jsonError(c, errorCode, errorMessage)
return
}
id := c.Query("doc_ids")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": errors.New("invalid document id"),
})
return
}
result, err := h.documentService.GetThumbnail(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": fmt.Errorf("thumbnail not found"),
})
return
}
if result.Thumbnail != nil && *result.Thumbnail != "" {
newThumbURL := fmt.Sprintf("/api/v1/documents/images/%s-%s", result.KbID, *result.Thumbnail)
result.Thumbnail = &newThumbURL
}
c.JSON(http.StatusOK, gin.H{
"code": common.CodeSuccess,
"data": map[string]interface{}{result.ID: result.Thumbnail},
"message": "success",
})
}
// GetDocumentImage returns a document image from object storage.
func (h *DocumentHandler) GetDocumentImage(c *gin.Context) {
imageID := c.Param("image_id")
data, err := h.documentService.GetDocumentImage(imageID)
if err != nil {
jsonError(c, common.CodeDataError, "Image not found.")
return
}
contentType := mime.TypeByExtension(strings.ToLower(filepath.Ext(imageID)))
if contentType == "" {
contentType = "image/JPEG"
}
c.Data(http.StatusOK, contentType, data)
}
// UpdateDocument update document
// @Summary Update Document
// @Description Update document info

View File

@@ -116,6 +116,9 @@ func (r *Router) Setup(engine *gin.Engine) {
// Register
apiNoAuth.POST("/users", r.userHandler.Register)
// Document images are embedded directly in pages and match Python's public route.
apiNoAuth.GET("/documents/images/:image_id", r.documentHandler.GetDocumentImage)
}
// Protected routes
@@ -411,6 +414,8 @@ func (r *Router) Setup(engine *gin.Engine) {
doc.POST("/delete_meta", r.documentHandler.DeleteMeta) // Internal API only for GO
}
v1.GET("/thumbnails", r.documentHandler.GetThumbnail)
// Chunk routes
chunk := v1.Group("/chunk")
{

View File

@@ -21,8 +21,10 @@ import (
"fmt"
"ragflow/internal/common"
"ragflow/internal/entity"
"ragflow/internal/storage"
"regexp"
"sort"
"strings"
"time"
"ragflow/internal/dao"
@@ -102,6 +104,27 @@ type DocumentResponse struct {
UpdatedAt string `json:"updated_at"`
}
type ThumbnailResponse struct {
ID string `json:"id"`
Thumbnail *string `json:"thumbnail,omitempty"`
KbID string `json:"kb_id"`
}
// GetDocumentImage retrieves an image object from storage.
func (s *DocumentService) GetDocumentImage(imageID string) ([]byte, error) {
parts := strings.Split(imageID, "-")
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return nil, fmt.Errorf("Image not found.")
}
storageImpl := storage.GetStorageFactory().GetStorage()
if storageImpl == nil {
return nil, fmt.Errorf("storage not initialized")
}
return storageImpl.Get(parts[0], parts[1])
}
// CreateDocument create document
func (s *DocumentService) CreateDocument(req *CreateDocumentRequest) (*entity.Document, error) {
document := &entity.Document{
@@ -182,6 +205,19 @@ func (s *DocumentService) ListDocuments(page, pageSize int) ([]*DocumentResponse
return responses, total, nil
}
func (s *DocumentService) GetThumbnail(docID string) (*ThumbnailResponse, error) {
document, err := s.documentDAO.GetByID(docID)
if err != nil {
return nil, err
}
var result ThumbnailResponse
result.ID = document.ID
result.Thumbnail = document.Thumbnail
result.KbID = document.KbID
return &result, nil
}
// ListDocumentsByDatasetID list documents by knowledge base ID
func (s *DocumentService) ListDocumentsByDatasetID(kbID string, page, pageSize int) ([]*entity.DocumentListItem, int64, error) {
offset := (page - 1) * pageSize
@@ -282,7 +318,7 @@ func (s *DocumentService) ParseDocuments(datasetID, userID string, docIDs []stri
}
// Send task to message queue
}
common.Info(fmt.Sprintf("parse documents, dataset: %s, documents: %v", datasetID, docIDs))
@@ -425,7 +461,7 @@ func (s *DocumentService) DeleteDocumentAllMetadata(docID string) error {
// Build condition to match the document
condition := map[string]interface{}{
"id": docID,
"id": docID,
"kb_id": doc.KbID,
}