From f4a1f420c55a1cb771b6af96b6fb41bf3ca7e0a2 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 26 Aug 2023 14:31:05 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/apiv1.py | 3 +- app/api/endpoints/filebrowser.py | 119 +++++++++++++++++++++++++++++++ app/schemas/__init__.py | 1 + app/schemas/file.py | 18 +++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 app/api/endpoints/filebrowser.py create mode 100644 app/schemas/file.py diff --git a/app/api/apiv1.py b/app/api/apiv1.py index b0ac8af8..04a21128 100644 --- a/app/api/apiv1.py +++ b/app/api/apiv1.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from app.api.endpoints import login, user, site, message, webhook, subscribe, \ - media, douban, search, plugin, tmdb, history, system, download, dashboard, rss + media, douban, search, plugin, tmdb, history, system, download, dashboard, rss, filebrowser api_router = APIRouter() api_router.include_router(login.router, prefix="/login", tags=["login"]) @@ -20,3 +20,4 @@ api_router.include_router(plugin.router, prefix="/plugin", tags=["plugin"]) api_router.include_router(download.router, prefix="/download", tags=["download"]) api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"]) api_router.include_router(rss.router, prefix="/rss", tags=["rss"]) +api_router.include_router(filebrowser.router, prefix="/filebrowser", tags=["filebrowser"]) \ No newline at end of file diff --git a/app/api/endpoints/filebrowser.py b/app/api/endpoints/filebrowser.py new file mode 100644 index 00000000..b4ea3146 --- /dev/null +++ b/app/api/endpoints/filebrowser.py @@ -0,0 +1,119 @@ +import shutil +from pathlib import Path +from typing import Any, List + +from fastapi import APIRouter, Depends +from starlette.responses import FileResponse + +from app import schemas +from app.core.config import settings +from app.core.security import verify_token +from app.log import logger +from app.utils.system import SystemUtils + +router = APIRouter() + + +@router.get("/list", summary="所有插件", response_model=List[schemas.FileItem]) +def list_path(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 查询当前目录下所有目录和文件 + """ + if not path: + path = "/" + path_obj = Path(path) + if not path_obj.exists(): + logger.error(f"目录不存在:{path}") + return [] + ret_items = [] + # 如果是文件 + if path_obj.is_file(): + ret_items.append(schemas.FileItem( + type="file", + path=str(path_obj), + name=path_obj.name, + basename=path_obj.stem, + extension=path_obj.suffix, + size=path_obj.stat().st_size, + )) + return ret_items + # 扁历所有目录 + for item in SystemUtils.list_sub_directory(path_obj): + ret_items.append(schemas.FileItem( + type="dir", + path=str(item) + "/", + name=item.name, + basename=item.stem, + extension=item.suffix, + )) + # 遍历所有文件,不含子目录 + for item in SystemUtils.list_sub_files(path_obj, + settings.RMT_MEDIAEXT + + settings.RMT_SUBEXT + + [".jpg", ".png", ".nfo"]): + ret_items.append(schemas.FileItem( + type="file", + path=str(item), + name=item.name, + basename=item.stem, + extension=item.suffix, + size=item.stat().st_size, + )) + return ret_items + + +@router.get("/mkdir", summary="创建目录", response_model=schemas.Response) +def mkdir(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 创建目录 + """ + if not path: + return schemas.Response(success=False) + path_obj = Path(path) + if path_obj.exists(): + return schemas.Response(success=False) + path_obj.mkdir(parents=True, exist_ok=True) + return schemas.Response(success=True) + + +@router.get("/delete", summary="删除文件或目录", response_model=schemas.Response) +def delete(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 删除文件或目录 + """ + if not path: + return schemas.Response(success=False) + path_obj = Path(path) + if not path_obj.exists(): + return schemas.Response(success=True) + if path_obj.is_file(): + path_obj.unlink() + else: + shutil.rmtree(path_obj, ignore_errors=True) + return schemas.Response(success=True) + + +@router.get("/download", summary="下载文件或目录") +def download(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 下载文件或目录 + """ + if not path: + return schemas.Response(success=False) + path_obj = Path(path) + if not path_obj.exists(): + return schemas.Response(success=False) + if path_obj.is_file(): + # 做为文件流式下载 + return FileResponse(path_obj, headers={ + "Content-Disposition": f"attachment; filename={path_obj.name}" + }, filename=path_obj.name) + else: + # 做为压缩包下载 + shutil.make_archive(base_name=path_obj.stem, format="zip", root_dir=path_obj) + reponse = FileResponse(f"{path_obj.stem}.zip", headers={ + "Content-Disposition": f"attachment; filename={path_obj.stem}.zip" + }, filename=f"{path_obj.stem}.zip") + # 删除压缩包 + Path(f"{path_obj.stem}.zip").unlink() + return reponse diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py index 200b3d92..a88ca0cd 100644 --- a/app/schemas/__init__.py +++ b/app/schemas/__init__.py @@ -13,3 +13,4 @@ from .message import * from .tmdb import * from .transfer import * from .rss import * +from .file import * diff --git a/app/schemas/file.py b/app/schemas/file.py new file mode 100644 index 00000000..d34ee7bd --- /dev/null +++ b/app/schemas/file.py @@ -0,0 +1,18 @@ +from typing import Optional + +from pydantic import BaseModel + + +class FileItem(BaseModel): + # 类型 dir/file + type: Optional[str] = None + # 文件路径 + path: Optional[str] = None + # 文件名 + name: Optional[str] = None + # 文件名 + basename: Optional[str] = None + # 文件后缀 + extension: Optional[str] = None + # 文件大小 + size: Optional[int] = None