You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

77 lines
2.8 KiB

from fastapi import APIRouter, Depends, Header, Request, status
from fastapi.responses import Response
from app.api.dependencies import get_upload_service
from app.schemas.common import ErrorResponse
from app.services.upload_service import CreateUploadInput, UploadService
router = APIRouter()
TUS_VERSION = "1.0.0"
@router.post("", status_code=status.HTTP_201_CREATED, responses={409: {"model": ErrorResponse}})
async def create_tus_upload(
upload_length: int = Header(alias="Upload-Length"),
upload_metadata: str | None = Header(default=None, alias="Upload-Metadata"),
tus_resumable: str = Header(alias="Tus-Resumable"),
service: UploadService = Depends(get_upload_service),
) -> Response:
if tus_resumable != TUS_VERSION:
return Response(status_code=status.HTTP_412_PRECONDITION_FAILED)
upload_session = await service.create_upload(
CreateUploadInput(upload_length=upload_length, upload_metadata=upload_metadata)
)
return Response(
status_code=status.HTTP_201_CREATED,
headers={
"Location": upload_session.tus_upload_url or f"/files/{upload_session.id}",
"Tus-Resumable": TUS_VERSION,
},
)
@router.head("/{upload_id}")
async def head_tus_upload(
upload_id: str,
tus_resumable: str = Header(alias="Tus-Resumable"),
service: UploadService = Depends(get_upload_service),
) -> Response:
if tus_resumable != TUS_VERSION:
return Response(status_code=status.HTTP_412_PRECONDITION_FAILED)
upload_session = await service.get_upload(upload_id)
return Response(
status_code=status.HTTP_200_OK,
headers={
"Tus-Resumable": TUS_VERSION,
"Upload-Offset": str(upload_session.received_size_bytes),
"Upload-Length": str(upload_session.total_size_bytes),
},
)
@router.patch("/{upload_id}", status_code=status.HTTP_204_NO_CONTENT, responses={409: {"model": ErrorResponse}})
async def patch_tus_upload(
upload_id: str,
request: Request,
upload_offset: int = Header(alias="Upload-Offset"),
tus_resumable: str = Header(alias="Tus-Resumable"),
content_type: str = Header(alias="Content-Type"),
service: UploadService = Depends(get_upload_service),
) -> Response:
if tus_resumable != TUS_VERSION:
return Response(status_code=status.HTTP_412_PRECONDITION_FAILED)
if content_type != "application/offset+octet-stream":
return Response(status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
chunk = await request.body()
upload_session = await service.append_upload_bytes(upload_id, upload_offset, chunk)
return Response(
status_code=status.HTTP_204_NO_CONTENT,
headers={
"Tus-Resumable": TUS_VERSION,
"Upload-Offset": str(upload_session.received_size_bytes),
},
)