diff --git a/src/app/models.py b/src/app/models.py index 6fec260..1420c1b 100644 --- a/src/app/models.py +++ b/src/app/models.py @@ -16,4 +16,4 @@ class CachedFile(ormar.Model): id: int = ormar.Integer(primary_key=True) # type: ignore object_id: int = ormar.Integer() # type: ignore object_type: str = ormar.String(max_length=8) # type: ignore - data = ormar.JSON() + data: dict = ormar.JSON() # type: ignore diff --git a/src/app/services/cache_updater.py b/src/app/services/cache_updater.py index 7271295..7764441 100644 --- a/src/app/services/cache_updater.py +++ b/src/app/services/cache_updater.py @@ -5,7 +5,7 @@ from typing import Optional from app.models import CachedFile from app.services.caption_getter import get_caption from app.services.downloader import download -from app.services.files_uploader import upload_file +from app.services.files_client import upload_file from app.services.library_client import get_books, get_book, Book diff --git a/src/app/services/downloader.py b/src/app/services/downloader.py index 9df2d6d..62e5216 100644 --- a/src/app/services/downloader.py +++ b/src/app/services/downloader.py @@ -25,3 +25,19 @@ async def download( name = content_disposition.replace("attachment; filename=", "") return response.content, name + + +async def get_filename(book_id: int, file_type: str) -> Optional[str]: + headers = {"Authorization": env_config.DOWNLOADER_API_KEY} + + async with httpx.AsyncClient() as client: + response = await client.get( + f"{env_config.DOWNLOADER_URL}/filename/{book_id}/{file_type}", + headers=headers, + timeout=5 * 60, + ) + + if response.status_code != 200: + return None + + return response.text diff --git a/src/app/services/files_uploader.py b/src/app/services/files_client.py similarity index 59% rename from src/app/services/files_uploader.py rename to src/app/services/files_client.py index 84a31e3..dc20961 100644 --- a/src/app/services/files_uploader.py +++ b/src/app/services/files_client.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Optional import httpx from pydantic import BaseModel @@ -29,3 +30,19 @@ async def upload_file(content: bytes, filename: str, caption: str) -> UploadedFi ) return UploadedFile.parse_obj(response.json()) + + +async def download_file(chat_id: int, message_id: int) -> Optional[bytes]: + headers = {"Authorization": env_config.FILES_SERVER_API_KEY} + + async with httpx.AsyncClient(timeout=60) as client: + response = await client.get( + f"{env_config.FILES_SERVER_URL}" + f"/api/v1/files/download_by_message/{chat_id}/{message_id}", + headers=headers, + ) + + if response.status_code != 200: + return None + + return response.content diff --git a/src/app/views.py b/src/app/views.py index bd78ecd..8849f52 100644 --- a/src/app/views.py +++ b/src/app/views.py @@ -1,11 +1,15 @@ from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, status +from starlette.responses import Response + from asyncpg import exceptions from app.depends import check_token from app.models import CachedFile as CachedFileDB from app.serializers import CachedFile, CreateCachedFile from app.services.cache_updater import CacheUpdater +from app.services.downloader import get_filename +from app.services.files_client import download_file as download_file_from_cache router = APIRouter( @@ -28,6 +32,34 @@ async def get_cached_file(object_id: int, object_type: str): return cached_file +@router.get("/download/{object_id}/{object_type}") +async def download_cached_file(object_id: int, object_type: str): + cached_file = await CachedFileDB.objects.get_or_none( + object_id=object_id, object_type=object_type + ) + + if not cached_file: + cached_file = await CacheUpdater.cache_file(object_id, object_type) + + if not cached_file: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + cache_data = cached_file.data + + data = await download_file_from_cache( + cache_data["chat_id"], cache_data["message_id"] + ) + + if data is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + filename = await get_filename(object_id, object_type) + + return Response( + data, headers={"Content-Disposition": f"attachment; filename={filename}"} + ) + + @router.delete("/{object_id}/{object_type}", response_model=CachedFile) async def delete_cached_file(object_id: int, object_type: str): cached_file = await CachedFileDB.objects.get(