from __future__ import annotations from my_modules.app.setup import LIMITER from my_modules.decoratory.header import token_required from quart_common.web.wide_event import add_wide_event_context from quart import Blueprint, abort, current_app, send_file api_download_bp = Blueprint('api_download', __name__) def _last_modified_from_file(file_data: dict): uploaded_at = file_data.get('uploaded_at') if uploaded_at is None: return None try: return int(uploaded_at) / 1000 except (TypeError, ValueError): return None @api_download_bp.get('/api/files//download') @LIMITER.limit('60 per minute;1000 per hour;') @token_required(['files', 'mesh']) async def api_file_download(file_id: str, user: dict): add_wide_event_context(nanoshare={'operation': 'api_file_download', 'file_id': file_id}) file_data = await current_app.convex.get_file_informations(file_id=file_id, user_id=user['sub']) if not file_data: add_wide_event_context(nanoshare={'operation_status': 'not_found'}) abort(404) if file_data.get('expired'): add_wide_event_context(nanoshare={'operation_status': 'expired'}) abort(410) storage_id = file_data.get('db_image_url') if not storage_id: add_wide_event_context(nanoshare={'operation_status': 'missing_storage'}) abort(404) content_type = file_data.get('content_type') or 'application/octet-stream' file_name = file_data.get('file_name') or file_id add_wide_event_context(nanoshare={'operation_status': 'served', 'content_type': content_type}) response = await send_file( filename_or_io=await current_app.convex.get_from_storage(storage_id), mimetype=content_type, as_attachment=True, attachment_filename=file_name, conditional=True, cache_timeout=0, last_modified=_last_modified_from_file(file_data), ) response.headers['Cache-Control'] = 'private, no-store' return response