File size: 2,604 Bytes
fba6023
 
 
 
 
 
 
 
 
3493993
fba6023
3493993
fba6023
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3493993
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fba6023
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from __future__ import annotations

from collections.abc import AsyncIterator
from pathlib import Path

import aiofiles
from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse

from app.core.exceptions import NotFoundError
from app.core.response import SuccessResponse
from app.security.assets import CanonicalAssetNotFoundError
from app.services.media_service import MediaProcessor, Operation

router = APIRouter(tags=["media"])


def get_processor(request: Request) -> MediaProcessor:
    return request.app.state.container.processor


async def execute_operation(request: Request, name: str, operation: Operation) -> SuccessResponse:
    request.state.operation = name
    processor = get_processor(request)
    resolved = await processor.resolver.resolve(request)
    return await processor.run(resolved, name, operation)


async def stream_file(path: Path) -> AsyncIterator[bytes]:
    async with aiofiles.open(path, "rb") as media:
        while chunk := await media.read(1024 * 1024):
            yield chunk


@router.get("/v1/media/{request_id}/{filename}", name="download_media")
async def download_media(request: Request, request_id: str, filename: str) -> StreamingResponse:
    request.state.operation = "media.download"
    container = request.app.state.container
    # The filesystem locator is not an authorization token. In authenticated
    # deployments an output must have been issued by the pipeline to the
    # caller's authoritative workspace before it can be downloaded.
    if container.settings.auth_enabled:
        context = getattr(request.state, "auth", None)
        if context is None or not context.workspace_id:
            raise NotFoundError("Output file not found")
        try:
            asset = await container.assets.get_owned(
                workspace_id=context.workspace_id,
                request_id=request_id,
                filename=filename,
            )
            path = container.cleanup.resolve_download(request_id, asset.filename)
            await container.assets.verify_file(asset, path)
        except CanonicalAssetNotFoundError as exc:
            raise NotFoundError("Output file not found") from exc
    else:
        path = container.cleanup.resolve_download(request_id, filename)
    media_type = container.validator.infer_mime(path)
    headers = {
        "Content-Disposition": f'attachment; filename="{path.name}"',
        "Content-Length": str(path.stat().st_size),
        "X-Request-ID": request_id,
    }
    return StreamingResponse(stream_file(path), media_type=media_type, headers=headers)