Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>tags/1.7.2
| @@ -6,6 +6,6 @@ bp = Blueprint("service_api", __name__, url_prefix="/v1") | |||
| api = ExternalApi(bp) | |||
| from . import index | |||
| from .app import annotation, app, audio, completion, conversation, file, message, site, workflow | |||
| from .app import annotation, app, audio, completion, conversation, file, file_preview, message, site, workflow | |||
| from .dataset import dataset, document, hit_testing, metadata, segment, upload_file | |||
| from .workspace import models | |||
| @@ -107,3 +107,15 @@ class UnsupportedFileTypeError(BaseHTTPException): | |||
| error_code = "unsupported_file_type" | |||
| description = "File type not allowed." | |||
| code = 415 | |||
| class FileNotFoundError(BaseHTTPException): | |||
| error_code = "file_not_found" | |||
| description = "The requested file was not found." | |||
| code = 404 | |||
| class FileAccessDeniedError(BaseHTTPException): | |||
| error_code = "file_access_denied" | |||
| description = "Access to the requested file is denied." | |||
| code = 403 | |||
| @@ -0,0 +1,186 @@ | |||
| import logging | |||
| from urllib.parse import quote | |||
| from flask import Response | |||
| from flask_restful import Resource, reqparse | |||
| from controllers.service_api import api | |||
| from controllers.service_api.app.error import ( | |||
| FileAccessDeniedError, | |||
| FileNotFoundError, | |||
| ) | |||
| from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token | |||
| from extensions.ext_database import db | |||
| from extensions.ext_storage import storage | |||
| from models.model import App, EndUser, Message, MessageFile, UploadFile | |||
| logger = logging.getLogger(__name__) | |||
| class FilePreviewApi(Resource): | |||
| """ | |||
| Service API File Preview endpoint | |||
| Provides secure file preview/download functionality for external API users. | |||
| Files can only be accessed if they belong to messages within the requesting app's context. | |||
| """ | |||
| @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) | |||
| def get(self, app_model: App, end_user: EndUser, file_id: str): | |||
| """ | |||
| Preview/Download a file that was uploaded via Service API | |||
| Args: | |||
| app_model: The authenticated app model | |||
| end_user: The authenticated end user (optional) | |||
| file_id: UUID of the file to preview | |||
| Query Parameters: | |||
| user: Optional user identifier | |||
| as_attachment: Boolean, whether to download as attachment (default: false) | |||
| Returns: | |||
| Stream response with file content | |||
| Raises: | |||
| FileNotFoundError: File does not exist | |||
| FileAccessDeniedError: File access denied (not owned by app) | |||
| """ | |||
| file_id = str(file_id) | |||
| # Parse query parameters | |||
| parser = reqparse.RequestParser() | |||
| parser.add_argument("as_attachment", type=bool, required=False, default=False, location="args") | |||
| args = parser.parse_args() | |||
| # Validate file ownership and get file objects | |||
| message_file, upload_file = self._validate_file_ownership(file_id, app_model.id) | |||
| # Get file content generator | |||
| try: | |||
| generator = storage.load(upload_file.key, stream=True) | |||
| except Exception as e: | |||
| raise FileNotFoundError(f"Failed to load file content: {str(e)}") | |||
| # Build response with appropriate headers | |||
| response = self._build_file_response(generator, upload_file, args["as_attachment"]) | |||
| return response | |||
| def _validate_file_ownership(self, file_id: str, app_id: str) -> tuple[MessageFile, UploadFile]: | |||
| """ | |||
| Validate that the file belongs to a message within the requesting app's context | |||
| Security validations performed: | |||
| 1. File exists in MessageFile table (was used in a conversation) | |||
| 2. Message belongs to the requesting app | |||
| 3. UploadFile record exists and is accessible | |||
| 4. File tenant matches app tenant (additional security layer) | |||
| Args: | |||
| file_id: UUID of the file to validate | |||
| app_id: UUID of the requesting app | |||
| Returns: | |||
| Tuple of (MessageFile, UploadFile) if validation passes | |||
| Raises: | |||
| FileNotFoundError: File or related records not found | |||
| FileAccessDeniedError: File does not belong to the app's context | |||
| """ | |||
| try: | |||
| # Input validation | |||
| if not file_id or not app_id: | |||
| raise FileAccessDeniedError("Invalid file or app identifier") | |||
| # First, find the MessageFile that references this upload file | |||
| message_file = db.session.query(MessageFile).where(MessageFile.upload_file_id == file_id).first() | |||
| if not message_file: | |||
| raise FileNotFoundError("File not found in message context") | |||
| # Get the message and verify it belongs to the requesting app | |||
| message = ( | |||
| db.session.query(Message).where(Message.id == message_file.message_id, Message.app_id == app_id).first() | |||
| ) | |||
| if not message: | |||
| raise FileAccessDeniedError("File access denied: not owned by requesting app") | |||
| # Get the actual upload file record | |||
| upload_file = db.session.query(UploadFile).where(UploadFile.id == file_id).first() | |||
| if not upload_file: | |||
| raise FileNotFoundError("Upload file record not found") | |||
| # Additional security: verify tenant isolation | |||
| app = db.session.query(App).where(App.id == app_id).first() | |||
| if app and upload_file.tenant_id != app.tenant_id: | |||
| raise FileAccessDeniedError("File access denied: tenant mismatch") | |||
| return message_file, upload_file | |||
| except (FileNotFoundError, FileAccessDeniedError): | |||
| # Re-raise our custom exceptions | |||
| raise | |||
| except Exception as e: | |||
| # Log unexpected errors for debugging | |||
| logger.exception( | |||
| "Unexpected error during file ownership validation", | |||
| extra={"file_id": file_id, "app_id": app_id, "error": str(e)}, | |||
| ) | |||
| raise FileAccessDeniedError("File access validation failed") | |||
| def _build_file_response(self, generator, upload_file: UploadFile, as_attachment: bool = False) -> Response: | |||
| """ | |||
| Build Flask Response object with appropriate headers for file streaming | |||
| Args: | |||
| generator: File content generator from storage | |||
| upload_file: UploadFile database record | |||
| as_attachment: Whether to set Content-Disposition as attachment | |||
| Returns: | |||
| Flask Response object with streaming file content | |||
| """ | |||
| response = Response( | |||
| generator, | |||
| mimetype=upload_file.mime_type, | |||
| direct_passthrough=True, | |||
| headers={}, | |||
| ) | |||
| # Add Content-Length if known | |||
| if upload_file.size and upload_file.size > 0: | |||
| response.headers["Content-Length"] = str(upload_file.size) | |||
| # Add Accept-Ranges header for audio/video files to support seeking | |||
| if upload_file.mime_type in [ | |||
| "audio/mpeg", | |||
| "audio/wav", | |||
| "audio/mp4", | |||
| "audio/ogg", | |||
| "audio/flac", | |||
| "audio/aac", | |||
| "video/mp4", | |||
| "video/webm", | |||
| "video/quicktime", | |||
| "audio/x-m4a", | |||
| ]: | |||
| response.headers["Accept-Ranges"] = "bytes" | |||
| # Set Content-Disposition for downloads | |||
| if as_attachment and upload_file.name: | |||
| encoded_filename = quote(upload_file.name) | |||
| response.headers["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}" | |||
| # Override content-type for downloads to force download | |||
| response.headers["Content-Type"] = "application/octet-stream" | |||
| # Add caching headers for performance | |||
| response.headers["Cache-Control"] = "public, max-age=3600" # Cache for 1 hour | |||
| return response | |||
| # Register the API endpoint | |||
| api.add_resource(FilePreviewApi, "/files/<uuid:file_id>/preview") | |||
| @@ -0,0 +1,336 @@ | |||
| """ | |||
| Unit tests for Service API File Preview endpoint | |||
| """ | |||
| import uuid | |||
| from unittest.mock import Mock, patch | |||
| import pytest | |||
| from controllers.service_api.app.error import FileAccessDeniedError, FileNotFoundError | |||
| from controllers.service_api.app.file_preview import FilePreviewApi | |||
| from models.model import App, EndUser, Message, MessageFile, UploadFile | |||
| class TestFilePreviewApi: | |||
| """Test suite for FilePreviewApi""" | |||
| @pytest.fixture | |||
| def file_preview_api(self): | |||
| """Create FilePreviewApi instance for testing""" | |||
| return FilePreviewApi() | |||
| @pytest.fixture | |||
| def mock_app(self): | |||
| """Mock App model""" | |||
| app = Mock(spec=App) | |||
| app.id = str(uuid.uuid4()) | |||
| app.tenant_id = str(uuid.uuid4()) | |||
| return app | |||
| @pytest.fixture | |||
| def mock_end_user(self): | |||
| """Mock EndUser model""" | |||
| end_user = Mock(spec=EndUser) | |||
| end_user.id = str(uuid.uuid4()) | |||
| return end_user | |||
| @pytest.fixture | |||
| def mock_upload_file(self): | |||
| """Mock UploadFile model""" | |||
| upload_file = Mock(spec=UploadFile) | |||
| upload_file.id = str(uuid.uuid4()) | |||
| upload_file.name = "test_file.jpg" | |||
| upload_file.mime_type = "image/jpeg" | |||
| upload_file.size = 1024 | |||
| upload_file.key = "storage/key/test_file.jpg" | |||
| upload_file.tenant_id = str(uuid.uuid4()) | |||
| return upload_file | |||
| @pytest.fixture | |||
| def mock_message_file(self): | |||
| """Mock MessageFile model""" | |||
| message_file = Mock(spec=MessageFile) | |||
| message_file.id = str(uuid.uuid4()) | |||
| message_file.upload_file_id = str(uuid.uuid4()) | |||
| message_file.message_id = str(uuid.uuid4()) | |||
| return message_file | |||
| @pytest.fixture | |||
| def mock_message(self): | |||
| """Mock Message model""" | |||
| message = Mock(spec=Message) | |||
| message.id = str(uuid.uuid4()) | |||
| message.app_id = str(uuid.uuid4()) | |||
| return message | |||
| def test_validate_file_ownership_success( | |||
| self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message | |||
| ): | |||
| """Test successful file ownership validation""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = mock_app.id | |||
| # Set up the mocks | |||
| mock_upload_file.tenant_id = mock_app.tenant_id | |||
| mock_message.app_id = app_id | |||
| mock_message_file.upload_file_id = file_id | |||
| mock_message_file.message_id = mock_message.id | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock database queries | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query | |||
| mock_message, # Message query | |||
| mock_upload_file, # UploadFile query | |||
| mock_app, # App query for tenant validation | |||
| ] | |||
| # Execute the method | |||
| result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id) | |||
| # Assertions | |||
| assert result_message_file == mock_message_file | |||
| assert result_upload_file == mock_upload_file | |||
| def test_validate_file_ownership_file_not_found(self, file_preview_api): | |||
| """Test file ownership validation when MessageFile not found""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = str(uuid.uuid4()) | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock MessageFile not found | |||
| mock_db.session.query.return_value.where.return_value.first.return_value = None | |||
| # Execute and assert exception | |||
| with pytest.raises(FileNotFoundError) as exc_info: | |||
| file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert "File not found in message context" in str(exc_info.value) | |||
| def test_validate_file_ownership_access_denied(self, file_preview_api, mock_message_file): | |||
| """Test file ownership validation when Message not owned by app""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = str(uuid.uuid4()) | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock MessageFile found but Message not owned by app | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query - found | |||
| None, # Message query - not found (access denied) | |||
| ] | |||
| # Execute and assert exception | |||
| with pytest.raises(FileAccessDeniedError) as exc_info: | |||
| file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert "not owned by requesting app" in str(exc_info.value) | |||
| def test_validate_file_ownership_upload_file_not_found(self, file_preview_api, mock_message_file, mock_message): | |||
| """Test file ownership validation when UploadFile not found""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = str(uuid.uuid4()) | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock MessageFile and Message found but UploadFile not found | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query - found | |||
| mock_message, # Message query - found | |||
| None, # UploadFile query - not found | |||
| ] | |||
| # Execute and assert exception | |||
| with pytest.raises(FileNotFoundError) as exc_info: | |||
| file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert "Upload file record not found" in str(exc_info.value) | |||
| def test_validate_file_ownership_tenant_mismatch( | |||
| self, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message | |||
| ): | |||
| """Test file ownership validation with tenant mismatch""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = mock_app.id | |||
| # Set up tenant mismatch | |||
| mock_upload_file.tenant_id = "different_tenant_id" | |||
| mock_app.tenant_id = "app_tenant_id" | |||
| mock_message.app_id = app_id | |||
| mock_message_file.upload_file_id = file_id | |||
| mock_message_file.message_id = mock_message.id | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock database queries | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query | |||
| mock_message, # Message query | |||
| mock_upload_file, # UploadFile query | |||
| mock_app, # App query for tenant validation | |||
| ] | |||
| # Execute and assert exception | |||
| with pytest.raises(FileAccessDeniedError) as exc_info: | |||
| file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert "tenant mismatch" in str(exc_info.value) | |||
| def test_validate_file_ownership_invalid_input(self, file_preview_api): | |||
| """Test file ownership validation with invalid input""" | |||
| # Test with empty file_id | |||
| with pytest.raises(FileAccessDeniedError) as exc_info: | |||
| file_preview_api._validate_file_ownership("", "app_id") | |||
| assert "Invalid file or app identifier" in str(exc_info.value) | |||
| # Test with empty app_id | |||
| with pytest.raises(FileAccessDeniedError) as exc_info: | |||
| file_preview_api._validate_file_ownership("file_id", "") | |||
| assert "Invalid file or app identifier" in str(exc_info.value) | |||
| def test_build_file_response_basic(self, file_preview_api, mock_upload_file): | |||
| """Test basic file response building""" | |||
| mock_generator = Mock() | |||
| response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False) | |||
| # Check response properties | |||
| assert response.mimetype == mock_upload_file.mime_type | |||
| assert response.direct_passthrough is True | |||
| assert response.headers["Content-Length"] == str(mock_upload_file.size) | |||
| assert "Cache-Control" in response.headers | |||
| def test_build_file_response_as_attachment(self, file_preview_api, mock_upload_file): | |||
| """Test file response building with attachment flag""" | |||
| mock_generator = Mock() | |||
| response = file_preview_api._build_file_response(mock_generator, mock_upload_file, True) | |||
| # Check attachment-specific headers | |||
| assert "attachment" in response.headers["Content-Disposition"] | |||
| assert mock_upload_file.name in response.headers["Content-Disposition"] | |||
| assert response.headers["Content-Type"] == "application/octet-stream" | |||
| def test_build_file_response_audio_video(self, file_preview_api, mock_upload_file): | |||
| """Test file response building for audio/video files""" | |||
| mock_generator = Mock() | |||
| mock_upload_file.mime_type = "video/mp4" | |||
| response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False) | |||
| # Check Range support for media files | |||
| assert response.headers["Accept-Ranges"] == "bytes" | |||
| def test_build_file_response_no_size(self, file_preview_api, mock_upload_file): | |||
| """Test file response building when size is unknown""" | |||
| mock_generator = Mock() | |||
| mock_upload_file.size = 0 # Unknown size | |||
| response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False) | |||
| # Content-Length should not be set when size is unknown | |||
| assert "Content-Length" not in response.headers | |||
| @patch("controllers.service_api.app.file_preview.storage") | |||
| def test_get_method_integration( | |||
| self, mock_storage, file_preview_api, mock_app, mock_end_user, mock_upload_file, mock_message_file, mock_message | |||
| ): | |||
| """Test the full GET method integration (without decorator)""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = mock_app.id | |||
| # Set up mocks | |||
| mock_upload_file.tenant_id = mock_app.tenant_id | |||
| mock_message.app_id = app_id | |||
| mock_message_file.upload_file_id = file_id | |||
| mock_message_file.message_id = mock_message.id | |||
| mock_generator = Mock() | |||
| mock_storage.load.return_value = mock_generator | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock database queries | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query | |||
| mock_message, # Message query | |||
| mock_upload_file, # UploadFile query | |||
| mock_app, # App query for tenant validation | |||
| ] | |||
| with patch("controllers.service_api.app.file_preview.reqparse") as mock_reqparse: | |||
| # Mock request parsing | |||
| mock_parser = Mock() | |||
| mock_parser.parse_args.return_value = {"as_attachment": False} | |||
| mock_reqparse.RequestParser.return_value = mock_parser | |||
| # Test the core logic directly without Flask decorators | |||
| # Validate file ownership | |||
| result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert result_message_file == mock_message_file | |||
| assert result_upload_file == mock_upload_file | |||
| # Test file response building | |||
| response = file_preview_api._build_file_response(mock_generator, mock_upload_file, False) | |||
| assert response is not None | |||
| # Verify storage was called correctly | |||
| mock_storage.load.assert_not_called() # Since we're testing components separately | |||
| @patch("controllers.service_api.app.file_preview.storage") | |||
| def test_storage_error_handling( | |||
| self, mock_storage, file_preview_api, mock_app, mock_upload_file, mock_message_file, mock_message | |||
| ): | |||
| """Test storage error handling in the core logic""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = mock_app.id | |||
| # Set up mocks | |||
| mock_upload_file.tenant_id = mock_app.tenant_id | |||
| mock_message.app_id = app_id | |||
| mock_message_file.upload_file_id = file_id | |||
| mock_message_file.message_id = mock_message.id | |||
| # Mock storage error | |||
| mock_storage.load.side_effect = Exception("Storage error") | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock database queries for validation | |||
| mock_db.session.query.return_value.where.return_value.first.side_effect = [ | |||
| mock_message_file, # MessageFile query | |||
| mock_message, # Message query | |||
| mock_upload_file, # UploadFile query | |||
| mock_app, # App query for tenant validation | |||
| ] | |||
| # First validate file ownership works | |||
| result_message_file, result_upload_file = file_preview_api._validate_file_ownership(file_id, app_id) | |||
| assert result_message_file == mock_message_file | |||
| assert result_upload_file == mock_upload_file | |||
| # Test storage error handling | |||
| with pytest.raises(Exception) as exc_info: | |||
| mock_storage.load(mock_upload_file.key, stream=True) | |||
| assert "Storage error" in str(exc_info.value) | |||
| @patch("controllers.service_api.app.file_preview.logger") | |||
| def test_validate_file_ownership_unexpected_error_logging(self, mock_logger, file_preview_api): | |||
| """Test that unexpected errors are logged properly""" | |||
| file_id = str(uuid.uuid4()) | |||
| app_id = str(uuid.uuid4()) | |||
| with patch("controllers.service_api.app.file_preview.db") as mock_db: | |||
| # Mock database query to raise unexpected exception | |||
| mock_db.session.query.side_effect = Exception("Unexpected database error") | |||
| # Execute and assert exception | |||
| with pytest.raises(FileAccessDeniedError) as exc_info: | |||
| file_preview_api._validate_file_ownership(file_id, app_id) | |||
| # Verify error message | |||
| assert "File access validation failed" in str(exc_info.value) | |||
| # Verify logging was called | |||
| mock_logger.exception.assert_called_once_with( | |||
| "Unexpected error during file ownership validation", | |||
| extra={"file_id": file_id, "app_id": app_id, "error": "Unexpected database error"}, | |||
| ) | |||
| @@ -277,6 +277,85 @@ The text generation application offers non-session support and is ideal for tran | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='File Preview' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. | |||
| <i>Files can only be accessed if they belong to messages within the requesting application.</i> | |||
| ### Path Parameters | |||
| - `file_id` (string) Required | |||
| The unique identifier of the file to preview, obtained from the File Upload API response. | |||
| ### Query Parameters | |||
| - `as_attachment` (boolean) Optional | |||
| Whether to force download the file as an attachment. Default is `false` (preview in browser). | |||
| ### Response | |||
| Returns the file content with appropriate headers for browser display or download. | |||
| - `Content-Type` Set based on file mime type | |||
| - `Content-Length` File size in bytes (if available) | |||
| - `Content-Disposition` Set to "attachment" if `as_attachment=true` | |||
| - `Cache-Control` Caching headers for performance | |||
| - `Accept-Ranges` Set to "bytes" for audio/video files | |||
| ### Errors | |||
| - 400, `invalid_param`, abnormal parameter input | |||
| - 403, `file_access_denied`, file access denied or file does not belong to current application | |||
| - 404, `file_not_found`, file not found or has been deleted | |||
| - 500, internal server error | |||
| </Col> | |||
| <Col sticky> | |||
| ### Request Example | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download as Attachment | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### Response Headers Example | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - Image Preview' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download Response Headers | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - File Download' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/completion-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -276,6 +276,85 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='ファイルプレビュー' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。 | |||
| <i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i> | |||
| ### パスパラメータ | |||
| - `file_id` (string) 必須 | |||
| プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。 | |||
| ### クエリパラメータ | |||
| - `as_attachment` (boolean) オプション | |||
| ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。 | |||
| ### レスポンス | |||
| ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。 | |||
| - `Content-Type` ファイル MIME タイプに基づいて設定 | |||
| - `Content-Length` ファイルサイズ(バイト、利用可能な場合) | |||
| - `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定 | |||
| - `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー | |||
| - `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定 | |||
| ### エラー | |||
| - 400, `invalid_param`, パラメータ入力異常 | |||
| - 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません | |||
| - 404, `file_not_found`, ファイルが見つからないか削除されています | |||
| - 500, サーバー内部エラー | |||
| </Col> | |||
| <Col sticky> | |||
| ### リクエスト例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 添付ファイルとしてダウンロード | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### レスポンスヘッダー例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'ヘッダー - 画像プレビュー' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### ファイルダウンロードレスポンスヘッダー | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'ヘッダー - ファイルダウンロード' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/completion-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -252,6 +252,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='文件预览' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 | |||
| <i>文件只能在属于请求应用程序的消息范围内访问。</i> | |||
| ### 路径参数 | |||
| - `file_id` (string) 必需 | |||
| 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 | |||
| ### 查询参数 | |||
| - `as_attachment` (boolean) 可选 | |||
| 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 | |||
| ### 响应 | |||
| 返回带有适当浏览器显示或下载标头的文件内容。 | |||
| - `Content-Type` 根据文件 MIME 类型设置 | |||
| - `Content-Length` 文件大小(以字节为单位,如果可用) | |||
| - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" | |||
| - `Cache-Control` 用于性能的缓存标头 | |||
| - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" | |||
| ### 错误 | |||
| - 400, `invalid_param`, 参数输入异常 | |||
| - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 | |||
| - 404, `file_not_found`, 文件未找到或已被删除 | |||
| - 500, 服务内部错误 | |||
| </Col> | |||
| <Col sticky> | |||
| ### 请求示例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 作为附件下载 | |||
| <CodeGroup title="下载请求" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### 响应标头示例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - 图片预览' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### 文件下载响应标头 | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - 文件下载' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/completion-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -392,6 +392,85 @@ Chat applications support session persistence, allowing previous chat history to | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='File Preview' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. | |||
| <i>Files can only be accessed if they belong to messages within the requesting application.</i> | |||
| ### Path Parameters | |||
| - `file_id` (string) Required | |||
| The unique identifier of the file to preview, obtained from the File Upload API response. | |||
| ### Query Parameters | |||
| - `as_attachment` (boolean) Optional | |||
| Whether to force download the file as an attachment. Default is `false` (preview in browser). | |||
| ### Response | |||
| Returns the file content with appropriate headers for browser display or download. | |||
| - `Content-Type` Set based on file mime type | |||
| - `Content-Length` File size in bytes (if available) | |||
| - `Content-Disposition` Set to "attachment" if `as_attachment=true` | |||
| - `Cache-Control` Caching headers for performance | |||
| - `Accept-Ranges` Set to "bytes" for audio/video files | |||
| ### Errors | |||
| - 400, `invalid_param`, abnormal parameter input | |||
| - 403, `file_access_denied`, file access denied or file does not belong to current application | |||
| - 404, `file_not_found`, file not found or has been deleted | |||
| - 500, internal server error | |||
| </Col> | |||
| <Col sticky> | |||
| ### Request Example | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download as Attachment | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### Response Headers Example | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - Image Preview' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download Response Headers | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - File Download' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/chat-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -653,7 +732,7 @@ Chat applications support session persistence, allowing previous chat history to | |||
| - `message_files` (array[object]) Message files | |||
| - `id` (string) ID | |||
| - `type` (string) File type, image for images | |||
| - `url` (string) Preview image URL | |||
| - `url` (string) File preview URL, use the File Preview API (`/files/{file_id}/preview`) to access the file | |||
| - `belongs_to` (string) belongs to,user orassistant | |||
| - `answer` (string) Response message content | |||
| - `created_at` (timestamp) Creation timestamp, e.g., 1705395332 | |||
| @@ -654,7 +654,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| - `message_files` (array[object]) メッセージファイル | |||
| - `id` (string) ID | |||
| - `type` (string) ファイルタイプ、画像の場合はimage | |||
| - `url` (string) プレビュー画像URL | |||
| - `url` (string) ファイルプレビューURL、ファイルアクセスにはファイルプレビューAPI(`/files/{file_id}/preview`)を使用してください | |||
| - `belongs_to` (string) 所属、userまたはassistant | |||
| - `answer` (string) 応答メッセージ内容 | |||
| - `created_at` (timestamp) 作成タイムスタンプ、例:1705395332 | |||
| @@ -1422,6 +1422,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='ファイルプレビュー' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。 | |||
| <i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i> | |||
| ### パスパラメータ | |||
| - `file_id` (string) 必須 | |||
| プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。 | |||
| ### クエリパラメータ | |||
| - `as_attachment` (boolean) オプション | |||
| ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。 | |||
| ### レスポンス | |||
| ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。 | |||
| - `Content-Type` ファイル MIME タイプに基づいて設定 | |||
| - `Content-Length` ファイルサイズ(バイト、利用可能な場合) | |||
| - `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定 | |||
| - `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー | |||
| - `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定 | |||
| ### エラー | |||
| - 400, `invalid_param`, パラメータ入力異常 | |||
| - 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません | |||
| - 404, `file_not_found`, ファイルが見つからないか削除されています | |||
| - 500, サーバー内部エラー | |||
| </Col> | |||
| <Col sticky> | |||
| ### リクエスト例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL - ブラウザプレビュー' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 添付ファイルとしてダウンロード | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### レスポンスヘッダー例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'ヘッダー - 画像プレビュー' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### ダウンロードレスポンスヘッダー | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'ヘッダー - ファイルダウンロード' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/meta' | |||
| method='GET' | |||
| @@ -399,6 +399,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='文件预览' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 | |||
| <i>文件只能在属于请求应用程序的消息范围内访问。</i> | |||
| ### 路径参数 | |||
| - `file_id` (string) 必需 | |||
| 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 | |||
| ### 查询参数 | |||
| - `as_attachment` (boolean) 可选 | |||
| 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 | |||
| ### 响应 | |||
| 返回带有适当浏览器显示或下载标头的文件内容。 | |||
| - `Content-Type` 根据文件 MIME 类型设置 | |||
| - `Content-Length` 文件大小(以字节为单位,如果可用) | |||
| - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" | |||
| - `Cache-Control` 用于性能的缓存标头 | |||
| - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" | |||
| ### 错误 | |||
| - 400, `invalid_param`, 参数输入异常 | |||
| - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 | |||
| - 404, `file_not_found`, 文件未找到或已被删除 | |||
| - 500, 服务内部错误 | |||
| </Col> | |||
| <Col sticky> | |||
| ### 请求示例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 作为附件下载 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### 响应标头示例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - 图片预览' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### 文件下载响应标头 | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - 文件下载' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/chat-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -661,7 +741,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' | |||
| - `message_files` (array[object]) 消息文件 | |||
| - `id` (string) ID | |||
| - `type` (string) 文件类型,image 图片 | |||
| - `url` (string) 预览图片地址 | |||
| - `url` (string) 文件预览地址,使用文件预览 API (`/files/{file_id}/preview`) 访问文件 | |||
| - `belongs_to` (string) 文件归属方,user 或 assistant | |||
| - `answer` (string) 回答消息内容 | |||
| - `created_at` (timestamp) 创建时间 | |||
| @@ -356,6 +356,85 @@ Chat applications support session persistence, allowing previous chat history to | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='File Preview' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. | |||
| <i>Files can only be accessed if they belong to messages within the requesting application.</i> | |||
| ### Path Parameters | |||
| - `file_id` (string) Required | |||
| The unique identifier of the file to preview, obtained from the File Upload API response. | |||
| ### Query Parameters | |||
| - `as_attachment` (boolean) Optional | |||
| Whether to force download the file as an attachment. Default is `false` (preview in browser). | |||
| ### Response | |||
| Returns the file content with appropriate headers for browser display or download. | |||
| - `Content-Type` Set based on file mime type | |||
| - `Content-Length` File size in bytes (if available) | |||
| - `Content-Disposition` Set to "attachment" if `as_attachment=true` | |||
| - `Cache-Control` Caching headers for performance | |||
| - `Accept-Ranges` Set to "bytes" for audio/video files | |||
| ### Errors | |||
| - 400, `invalid_param`, abnormal parameter input | |||
| - 403, `file_access_denied`, file access denied or file does not belong to current application | |||
| - 404, `file_not_found`, file not found or has been deleted | |||
| - 500, internal server error | |||
| </Col> | |||
| <Col sticky> | |||
| ### Request Example | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download as Attachment | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### Response Headers Example | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - Image Preview' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download Response Headers | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - File Download' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/chat-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -617,7 +696,7 @@ Chat applications support session persistence, allowing previous chat history to | |||
| - `message_files` (array[object]) Message files | |||
| - `id` (string) ID | |||
| - `type` (string) File type, image for images | |||
| - `url` (string) Preview image URL | |||
| - `url` (string) File preview URL, use the File Preview API (`/files/{file_id}/preview`) to access the file | |||
| - `belongs_to` (string) belongs to,user or assistant | |||
| - `agent_thoughts` (array[object]) Agent thought(Empty if it's a Basic Assistant) | |||
| - `id` (string) Agent thought ID, every iteration has a unique agent thought ID | |||
| @@ -356,6 +356,85 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='ファイルプレビュー' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。 | |||
| <i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i> | |||
| ### パスパラメータ | |||
| - `file_id` (string) 必須 | |||
| プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。 | |||
| ### クエリパラメータ | |||
| - `as_attachment` (boolean) オプション | |||
| ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。 | |||
| ### レスポンス | |||
| ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。 | |||
| - `Content-Type` ファイル MIME タイプに基づいて設定 | |||
| - `Content-Length` ファイルサイズ(バイト、利用可能な場合) | |||
| - `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定 | |||
| - `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー | |||
| - `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定 | |||
| ### エラー | |||
| - 400, `invalid_param`, パラメータ入力異常 | |||
| - 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません | |||
| - 404, `file_not_found`, ファイルが見つからないか削除されています | |||
| - 500, サーバー内部エラー | |||
| </Col> | |||
| <Col sticky> | |||
| ### リクエスト例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 添付ファイルとしてダウンロード | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### レスポンスヘッダー例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - 画像プレビュー' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### ダウンロードレスポンスヘッダー | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - ファイルダウンロード' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/chat-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -618,7 +697,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| - `message_files` (array[object]) メッセージファイル | |||
| - `id` (string) ID | |||
| - `type` (string) ファイルタイプ、画像の場合はimage | |||
| - `url` (string) プレビュー画像URL | |||
| - `url` (string) ファイルプレビューURL、ファイルアクセスにはファイルプレビューAPI(`/files/{file_id}/preview`)を使用してください | |||
| - `belongs_to` (string) 所属、ユーザーまたはアシスタント | |||
| - `agent_thoughts` (array[object]) エージェントの思考(基本アシスタントの場合は空) | |||
| - `id` (string) エージェント思考ID、各反復には一意のエージェント思考IDがあります | |||
| @@ -371,6 +371,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='文件预览' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 | |||
| <i>文件只能在属于请求应用程序的消息范围内访问。</i> | |||
| ### 路径参数 | |||
| - `file_id` (string) 必需 | |||
| 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 | |||
| ### 查询参数 | |||
| - `as_attachment` (boolean) 可选 | |||
| 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 | |||
| ### 响应 | |||
| 返回带有适当浏览器显示或下载标头的文件内容。 | |||
| - `Content-Type` 根据文件 MIME 类型设置 | |||
| - `Content-Length` 文件大小(以字节为单位,如果可用) | |||
| - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" | |||
| - `Cache-Control` 用于性能的缓存标头 | |||
| - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" | |||
| ### 错误 | |||
| - 400, `invalid_param`, 参数输入异常 | |||
| - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 | |||
| - 404, `file_not_found`, 文件未找到或已被删除 | |||
| - 500, 服务内部错误 | |||
| </Col> | |||
| <Col sticky> | |||
| ### 请求示例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 作为附件下载 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\\\\n--header 'Authorization: Bearer {api_key}' \\\\\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### 响应标头示例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - 图片预览' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### 文件下载响应标头 | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - 文件下载' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/chat-messages/:task_id/stop' | |||
| method='POST' | |||
| @@ -631,7 +711,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' | |||
| - `message_files` (array[object]) 消息文件 | |||
| - `id` (string) ID | |||
| - `type` (string) 文件类型,image 图片 | |||
| - `url` (string) 预览图片地址 | |||
| - `url` (string) 文件预览地址,使用文件预览 API (`/files/{file_id}/preview`) 访问文件 | |||
| - `belongs_to` (string) 文件归属方,user 或 assistant | |||
| - `agent_thoughts` (array[object]) Agent思考内容(仅Agent模式下不为空) | |||
| - `id` (string) agent_thought ID,每一轮Agent迭代都会有一个唯一的id | |||
| @@ -747,6 +747,86 @@ Workflow applications offers non-session support and is ideal for translation, a | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='File Preview' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| Preview or download uploaded files. This endpoint allows you to access files that have been previously uploaded via the File Upload API. | |||
| <i>Files can only be accessed if they belong to messages within the requesting application.</i> | |||
| ### Path Parameters | |||
| - `file_id` (string) Required | |||
| The unique identifier of the file to preview, obtained from the File Upload API response. | |||
| ### Query Parameters | |||
| - `as_attachment` (boolean) Optional | |||
| Whether to force download the file as an attachment. Default is `false` (preview in browser). | |||
| ### Response | |||
| Returns the file content with appropriate headers for browser display or download. | |||
| - `Content-Type` Set based on file mime type | |||
| - `Content-Length` File size in bytes (if available) | |||
| - `Content-Disposition` Set to "attachment" if `as_attachment=true` | |||
| - `Cache-Control` Caching headers for performance | |||
| - `Accept-Ranges` Set to "bytes" for audio/video files | |||
| ### Errors | |||
| - 400, `invalid_param`, abnormal parameter input | |||
| - 403, `file_access_denied`, file access denied or file does not belong to current application | |||
| - 404, `file_not_found`, file not found or has been deleted | |||
| - 500, internal server error | |||
| </Col> | |||
| <Col sticky> | |||
| ### Request Example | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download as Attachment | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### Response Headers Example | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - Image Preview' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### Download Response Headers | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - File Download' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/workflows/logs' | |||
| method='GET' | |||
| @@ -742,6 +742,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='ファイルプレビュー' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| アップロードされたファイルをプレビューまたはダウンロードします。このエンドポイントを使用すると、以前にファイルアップロード API でアップロードされたファイルにアクセスできます。 | |||
| <i>ファイルは、リクエストしているアプリケーションのメッセージ範囲内にある場合のみアクセス可能です。</i> | |||
| ### パスパラメータ | |||
| - `file_id` (string) 必須 | |||
| プレビューするファイルの一意識別子。ファイルアップロード API レスポンスから取得します。 | |||
| ### クエリパラメータ | |||
| - `as_attachment` (boolean) オプション | |||
| ファイルを添付ファイルとして強制ダウンロードするかどうか。デフォルトは `false`(ブラウザでプレビュー)。 | |||
| ### レスポンス | |||
| ブラウザ表示またはダウンロード用の適切なヘッダー付きでファイル内容を返します。 | |||
| - `Content-Type` ファイル MIME タイプに基づいて設定 | |||
| - `Content-Length` ファイルサイズ(バイト、利用可能な場合) | |||
| - `Content-Disposition` `as_attachment=true` の場合は "attachment" に設定 | |||
| - `Cache-Control` パフォーマンス向上のためのキャッシュヘッダー | |||
| - `Accept-Ranges` 音声/動画ファイルの場合は "bytes" に設定 | |||
| ### エラー | |||
| - 400, `invalid_param`, パラメータ入力異常 | |||
| - 403, `file_access_denied`, ファイルアクセス拒否またはファイルが現在のアプリケーションに属していません | |||
| - 404, `file_not_found`, ファイルが見つからないか削除されています | |||
| - 500, サーバー内部エラー | |||
| </Col> | |||
| <Col sticky> | |||
| ### リクエスト例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL - ブラウザプレビュー' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 添付ファイルとしてダウンロード | |||
| <CodeGroup title="Download Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\n--header 'Authorization: Bearer {api_key}' \\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### レスポンスヘッダー例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'ヘッダー - 画像プレビュー' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### ダウンロードレスポンスヘッダー | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'ヘッダー - ファイルダウンロード' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/workflows/logs' | |||
| method='GET' | |||
| @@ -730,6 +730,85 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/files/:file_id/preview' | |||
| method='GET' | |||
| title='文件预览' | |||
| name='#file-preview' | |||
| /> | |||
| <Row> | |||
| <Col> | |||
| 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 | |||
| <i>文件只能在属于请求应用程序的消息范围内访问。</i> | |||
| ### 路径参数 | |||
| - `file_id` (string) 必需 | |||
| 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 | |||
| ### 查询参数 | |||
| - `as_attachment` (boolean) 可选 | |||
| 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 | |||
| ### 响应 | |||
| 返回带有适当浏览器显示或下载标头的文件内容。 | |||
| - `Content-Type` 根据文件 MIME 类型设置 | |||
| - `Content-Length` 文件大小(以字节为单位,如果可用) | |||
| - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" | |||
| - `Cache-Control` 用于性能的缓存标头 | |||
| - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" | |||
| ### 错误 | |||
| - 400, `invalid_param`, 参数输入异常 | |||
| - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 | |||
| - 404, `file_not_found`, 文件未找到或已被删除 | |||
| - 500, 服务内部错误 | |||
| </Col> | |||
| <Col sticky> | |||
| ### 请求示例 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \\\n--header 'Authorization: Bearer {api_key}'`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ | |||
| --header 'Authorization: Bearer {api_key}' | |||
| ``` | |||
| </CodeGroup> | |||
| ### 作为附件下载 | |||
| <CodeGroup title="Request" tag="GET" label="/files/:file_id/preview?as_attachment=true" targetCode={`curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \\\\\\n--header 'Authorization: Bearer {api_key}' \\\\\\n--output downloaded_file.png`}> | |||
| ```bash {{ title: 'cURL' }} | |||
| curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ | |||
| --header 'Authorization: Bearer {api_key}' \ | |||
| --output downloaded_file.png | |||
| ``` | |||
| </CodeGroup> | |||
| ### 响应标头示例 | |||
| <CodeGroup title="Response Headers"> | |||
| ```http {{ title: 'Headers - 图片预览' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| ### 文件下载响应标头 | |||
| <CodeGroup title="Download Response Headers"> | |||
| ```http {{ title: 'Headers - 文件下载' }} | |||
| Content-Type: image/png | |||
| Content-Length: 1024 | |||
| Content-Disposition: attachment; filename*=UTF-8''example.png | |||
| Cache-Control: public, max-age=3600 | |||
| ``` | |||
| </CodeGroup> | |||
| </Col> | |||
| </Row> | |||
| --- | |||
| <Heading | |||
| url='/workflows/logs' | |||
| method='GET' | |||