mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-16 12:47:19 +08:00
Refa: migrate document preview/download to RESTful API (#14633)
### What problem does this PR solve? migrate document preview/download to RESTful API ### Type of change - [x] Refactoring
This commit is contained in:
@@ -29,6 +29,8 @@ Deprecated APIs and their replacements:
|
||||
- POST /api/v1/file/convert -> POST /api/v1/files/link-to-datasets
|
||||
- GET /api/v1/file/* -> GET /api/v1/files*
|
||||
- POST /api/v1/file/* -> POST /api/v1/files*
|
||||
- GET /api/v1/document/get/{doc_id} -> GET /api/v1/documents/{doc_id}/preview
|
||||
- GET /api/v1/document/download/{doc_id} -> GET /api/v1/documents/{doc_id}/download
|
||||
- POST /api/v1/sessions/related_questions -> POST /api/v1/chat/recommandation
|
||||
- PUT (chunk update) -> PATCH (chunk update)
|
||||
"""
|
||||
@@ -394,6 +396,44 @@ async def deprecated_file_upload_info():
|
||||
tenant_id = current_user.id
|
||||
return await document_api.upload_info(tenant_id=tenant_id)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Document APIs
|
||||
# =============================================================================
|
||||
|
||||
@manager.route("/document/get/<doc_id>", methods=["GET"])
|
||||
@login_required
|
||||
async def deprecated_document_get(doc_id):
|
||||
"""
|
||||
Deprecated: Use GET /api/v1/documents/{doc_id}/preview instead.
|
||||
|
||||
Old path: GET /api/v1/document/get/{doc_id}
|
||||
New path: GET /api/v1/documents/{doc_id}/preview
|
||||
"""
|
||||
logging.warning(
|
||||
"API endpoint /api/v1/document/get/%s is deprecated. "
|
||||
"Please use /api/v1/documents/%s/preview instead.",
|
||||
doc_id, doc_id,
|
||||
)
|
||||
return await document_api.get(doc_id)
|
||||
|
||||
|
||||
@manager.route("/document/download/<doc_id>", methods=["GET"])
|
||||
@login_required
|
||||
async def deprecated_document_download(doc_id):
|
||||
"""
|
||||
Deprecated: Use GET /api/v1/documents/{doc_id}/download instead.
|
||||
|
||||
Old path: GET /api/v1/document/download/{doc_id}
|
||||
New path: GET /api/v1/documents/{doc_id}/download
|
||||
"""
|
||||
logging.warning(
|
||||
"API endpoint /api/v1/document/download/%s is deprecated. "
|
||||
"Please use /api/v1/documents/%s/download instead.",
|
||||
doc_id, doc_id,
|
||||
)
|
||||
return await document_api.download_attachment(doc_id=doc_id)
|
||||
|
||||
# =============================================================================
|
||||
# Agent Chat API
|
||||
# =============================================================================
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#
|
||||
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License
|
||||
#
|
||||
import re
|
||||
|
||||
from quart import make_response, request
|
||||
|
||||
from api.apps import current_user, login_required
|
||||
from api.db import FileType
|
||||
from api.db.services.document_service import DocumentService
|
||||
from api.db.services.file2document_service import File2DocumentService
|
||||
from api.utils.api_utils import (
|
||||
get_data_error_result,
|
||||
server_error_response,
|
||||
)
|
||||
from api.utils.web_utils import CONTENT_TYPE_MAP, apply_safe_file_response_headers
|
||||
from common import settings
|
||||
from common.misc_utils import thread_pool_exec
|
||||
|
||||
|
||||
@manager.route("/get/<doc_id>", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
async def get(doc_id):
|
||||
try:
|
||||
e, doc = DocumentService.get_by_id(doc_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="Document not found!")
|
||||
|
||||
b, n = File2DocumentService.get_storage_address(doc_id=doc_id)
|
||||
data = await thread_pool_exec(settings.STORAGE_IMPL.get, b, n)
|
||||
response = await make_response(data)
|
||||
|
||||
ext = re.search(r"\.([^.]+)$", doc.name.lower())
|
||||
ext = ext.group(1) if ext else None
|
||||
content_type = None
|
||||
if ext:
|
||||
fallback_prefix = "image" if doc.type == FileType.VISUAL.value else "application"
|
||||
content_type = CONTENT_TYPE_MAP.get(ext, f"{fallback_prefix}/{ext}")
|
||||
apply_safe_file_response_headers(response, content_type, ext)
|
||||
return response
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route("/download/<attachment_id>", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
async def download_attachment(attachment_id):
|
||||
try:
|
||||
ext = request.args.get("ext", "markdown")
|
||||
data = await thread_pool_exec(settings.STORAGE_IMPL.get, current_user.id, attachment_id)
|
||||
response = await make_response(data)
|
||||
content_type = CONTENT_TYPE_MAP.get(ext, f"application/{ext}")
|
||||
apply_safe_file_response_headers(response, content_type, ext)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
@@ -33,6 +33,7 @@ from api.db.services import duplicate_name
|
||||
from api.db.services.doc_metadata_service import DocMetadataService
|
||||
from api.db.db_models import Task
|
||||
from api.db.services.document_service import DocumentService
|
||||
from api.db.services.file2document_service import File2DocumentService
|
||||
from api.db.services.file_service import FileService
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.common.check_team_permission import check_kb_team_permission
|
||||
@@ -48,7 +49,7 @@ from common.constants import ParserType, RetCode, TaskStatus, SANDBOX_ARTIFACT_B
|
||||
from common.metadata_utils import convert_conditions, meta_filter, turn2jsonschema
|
||||
from common.misc_utils import get_uuid, thread_pool_exec
|
||||
from api.utils.file_utils import filename_type, thumbnail
|
||||
from api.utils.web_utils import html2pdf, is_valid_url, apply_safe_file_response_headers
|
||||
from api.utils.web_utils import CONTENT_TYPE_MAP, html2pdf, is_valid_url, apply_safe_file_response_headers
|
||||
from common.ssrf_guard import assert_url_is_safe
|
||||
from rag.nlp import search
|
||||
|
||||
@@ -1854,3 +1855,46 @@ async def batch_update_document_status(tenant_id, dataset_id):
|
||||
if has_error:
|
||||
return get_json_result(data=result, message="Partial failure", code=RetCode.SERVER_ERROR)
|
||||
return get_json_result(data=result)
|
||||
|
||||
@manager.route("/documents/<doc_id>/preview", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
async def get(doc_id):
|
||||
try:
|
||||
e, doc = DocumentService.get_by_id(doc_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="Document not found!")
|
||||
|
||||
b, n = File2DocumentService.get_storage_address(doc_id=doc_id)
|
||||
data = await thread_pool_exec(settings.STORAGE_IMPL.get, b, n)
|
||||
response = await make_response(data)
|
||||
|
||||
ext = re.search(r"\.([^.]+)$", doc.name.lower())
|
||||
ext = ext.group(1) if ext else None
|
||||
content_type = None
|
||||
if ext:
|
||||
fallback_prefix = "image" if doc.type == FileType.VISUAL.value else "application"
|
||||
content_type = CONTENT_TYPE_MAP.get(ext, f"{fallback_prefix}/{ext}")
|
||||
apply_safe_file_response_headers(response, content_type, ext)
|
||||
return response
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route("/documents/<doc_id>/download", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
@add_tenant_id_to_kwargs
|
||||
async def download_attachment(tenant_id=None, doc_id=None, attachment_id=None):
|
||||
try:
|
||||
# Keep backward compatibility with older callers and unit tests that still
|
||||
# pass `attachment_id` instead of the route parameter name.
|
||||
doc_id = doc_id or attachment_id
|
||||
ext = request.args.get("ext", "markdown")
|
||||
data = await thread_pool_exec(settings.STORAGE_IMPL.get, tenant_id, doc_id)
|
||||
response = await make_response(data)
|
||||
content_type = CONTENT_TYPE_MAP.get(ext, f"application/{ext}")
|
||||
apply_safe_file_response_headers(response, content_type, ext)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
Reference in New Issue
Block a user