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
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),
|
|
},
|
|
)
|
|
|