dify/api/services/upload_file_delivery_service.py

116 lines
3.3 KiB
Python

"""Application service for delivering uploaded files through public file endpoints."""
from collections.abc import Iterator
from typing import NamedTuple, Protocol
from constants import IMAGE_EXTENSIONS
from graphon.file import helpers as file_helpers
from services.errors.file import UnsupportedFileTypeError
class UploadFileDeliveryNotFoundError(LookupError):
pass
class UploadFileDeliveryRecord(NamedTuple):
key: str
name: str
size: int
extension: str
mime_type: str | None
class UploadFileDeliveryQuery(Protocol):
def get_by_id(self, *, file_id: str) -> UploadFileDeliveryRecord | None: ...
def get_workspace_logo(self, *, workspace_id: str) -> UploadFileDeliveryRecord | None: ...
class UploadFileStorage(Protocol):
def load_stream(self, filename: str) -> Iterator[bytes]: ...
def load_once(self, filename: str) -> bytes: ...
class UploadFileDelivery(NamedTuple):
content: bytes | Iterator[bytes]
file: UploadFileDeliveryRecord
class UploadFileDeliveryService:
def __init__(
self,
*,
files: UploadFileDeliveryQuery,
storage: UploadFileStorage,
) -> None:
self._files = files
self._storage = storage
def get_signed_image_preview(
self,
*,
file_id: str,
timestamp: str,
nonce: str,
sign: str,
) -> UploadFileDelivery:
if not file_helpers.verify_image_signature(
upload_file_id=file_id,
timestamp=timestamp,
nonce=nonce,
sign=sign,
):
raise UploadFileDeliveryNotFoundError("File not found or signature is invalid")
file = self._get_file(file_id=file_id)
self._ensure_image(file=file)
return UploadFileDelivery(
content=self._storage.load_stream(file.key),
file=file,
)
def get_signed_file_preview(
self,
*,
file_id: str,
timestamp: str,
nonce: str,
sign: str,
) -> UploadFileDelivery:
if not file_helpers.verify_file_signature(
upload_file_id=file_id,
timestamp=timestamp,
nonce=nonce,
sign=sign,
):
raise UploadFileDeliveryNotFoundError("File not found or signature is invalid")
file = self._get_file(file_id=file_id)
return UploadFileDelivery(
content=self._storage.load_stream(file.key),
file=file,
)
def get_workspace_webapp_logo(self, *, workspace_id: str) -> UploadFileDelivery:
file = self._files.get_workspace_logo(workspace_id=workspace_id)
if file is None:
raise UploadFileDeliveryNotFoundError("File not found or signature is invalid")
self._ensure_image(file=file)
return UploadFileDelivery(
content=self._storage.load_once(file.key),
file=file,
)
def _get_file(self, *, file_id: str) -> UploadFileDeliveryRecord:
file = self._files.get_by_id(file_id=file_id)
if file is None:
raise UploadFileDeliveryNotFoundError("File not found or signature is invalid")
return file
@staticmethod
def _ensure_image(*, file: UploadFileDeliveryRecord) -> None:
if file.extension.lower() not in IMAGE_EXTENSIONS:
raise UnsupportedFileTypeError()