from __future__ import annotations from fastapi import APIRouter, Depends, File, Form, Query, Request, UploadFile from fastapi.responses import StreamingResponse from starlette.background import BackgroundTask from backend.app.api.schemas import ArchivePrepareRequest, DeleteRequest, DeleteResponse, FileInfoResponse, MkdirRequest, MkdirResponse, RenameRequest, RenameResponse, SaveRequest, SaveResponse, TaskCreateResponse, TaskDetailResponse, UploadResponse, ViewResponse from backend.app.dependencies import get_archive_download_task_service, get_file_ops_service from backend.app.services.archive_download_task_service import ArchiveDownloadTaskService from backend.app.services.file_ops_service import FileOpsService router = APIRouter(prefix="/files") @router.post("/mkdir", response_model=MkdirResponse) async def mkdir( request: MkdirRequest, service: FileOpsService = Depends(get_file_ops_service), ) -> MkdirResponse: return service.mkdir(parent_path=request.parent_path, name=request.name) @router.post("/rename", response_model=RenameResponse) async def rename( request: RenameRequest, service: FileOpsService = Depends(get_file_ops_service), ) -> RenameResponse: return service.rename(path=request.path, new_name=request.new_name) @router.post("/delete", response_model=DeleteResponse) async def delete( request: DeleteRequest, service: FileOpsService = Depends(get_file_ops_service), ) -> DeleteResponse: return service.delete(path=request.path, recursive=request.recursive) @router.post("/upload", response_model=UploadResponse) async def upload( target_path: str = Form(...), overwrite: bool = Form(False), file: UploadFile = File(...), service: FileOpsService = Depends(get_file_ops_service), ) -> UploadResponse: return service.upload(target_path=target_path, upload_file=file, overwrite=overwrite) @router.get("/view", response_model=ViewResponse) async def view( path: str, for_edit: bool = False, service: FileOpsService = Depends(get_file_ops_service), ) -> ViewResponse: return service.view(path=path, for_edit=for_edit) @router.get("/info", response_model=FileInfoResponse) async def info( path: str, service: FileOpsService = Depends(get_file_ops_service), ) -> FileInfoResponse: return service.info(path=path) @router.get("/download") async def download( path: list[str] = Query(...), service: FileOpsService = Depends(get_file_ops_service), ) -> StreamingResponse: prepared = service.prepare_download(paths=path) response = StreamingResponse( prepared["content"], headers=prepared["headers"], media_type=prepared["content_type"], ) if prepared.get("cleanup"): response.background = BackgroundTask(prepared["cleanup"]) return response @router.post("/download/archive-prepare", response_model=TaskCreateResponse, status_code=202) async def archive_prepare( request: ArchivePrepareRequest, service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service), ) -> TaskCreateResponse: return service.create_archive_prepare_task(paths=request.paths) @router.get("/download/archive/{task_id}") async def archive_download( task_id: str, service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service), ) -> StreamingResponse: prepared = service.prepare_ready_archive_download(task_id=task_id) return StreamingResponse( prepared["content"], headers=prepared["headers"], media_type=prepared["content_type"], ) @router.post("/download/archive/{task_id}/cancel", response_model=TaskDetailResponse) async def archive_cancel( task_id: str, service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service), ) -> TaskDetailResponse: return TaskDetailResponse(**service.cancel_archive_prepare_task(task_id=task_id)) @router.get("/video") async def video( path: str, request: Request, service: FileOpsService = Depends(get_file_ops_service), ) -> StreamingResponse: prepared = service.prepare_video_stream(path=path, range_header=request.headers.get("range")) return StreamingResponse( prepared["content"], status_code=prepared["status_code"], headers=prepared["headers"], media_type=prepared["content_type"], ) @router.get("/pdf") async def pdf( path: str, service: FileOpsService = Depends(get_file_ops_service), ) -> StreamingResponse: prepared = service.prepare_pdf_stream(path=path) return StreamingResponse( prepared["content"], headers=prepared["headers"], media_type=prepared["content_type"], ) @router.get("/image") async def image( path: str, service: FileOpsService = Depends(get_file_ops_service), ) -> StreamingResponse: prepared = service.prepare_image_stream(path=path) return StreamingResponse( prepared["content"], headers=prepared["headers"], media_type=prepared["content_type"], ) @router.get("/thumbnail") async def thumbnail( path: str, service: FileOpsService = Depends(get_file_ops_service), ) -> StreamingResponse: prepared = service.prepare_thumbnail_stream(path=path) return StreamingResponse( prepared["content"], headers=prepared["headers"], media_type=prepared["content_type"], ) @router.post("/save", response_model=SaveResponse) async def save( request: SaveRequest, service: FileOpsService = Depends(get_file_ops_service), ) -> SaveResponse: return service.save( path=request.path, content=request.content, expected_modified=request.expected_modified, )