From eb3d2843eb6a0b1f28cda17939eee7ca581785b8 Mon Sep 17 00:00:00 2001 From: Yuerchu Date: Wed, 18 Jun 2025 02:14:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=83=A8=E5=88=86API?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E4=BB=A5=E5=8F=8A=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 12 + README.md | 29 +- main.py | 29 ++ middleware/auth.py | 30 ++ models/model.py | 215 ++++++++++++++ models/response.py | 7 + pkg/JWT/jwt.py | 9 + pkg/conf/appmeta.py | 103 +++++++ routers/controllers/admin.py | 487 +++++++++++++++++++++++++++++++ routers/controllers/aria2.py | 107 +++++++ routers/controllers/callback.py | 279 ++++++++++++++++++ routers/controllers/directory.py | 41 +++ routers/controllers/file.py | 381 ++++++++++++++++++++++++ routers/controllers/object.py | 86 ++++++ routers/controllers/share.py | 306 +++++++++++++++++++ routers/controllers/site.py | 52 ++++ routers/controllers/tag.py | 56 ++++ routers/controllers/user.py | 370 +++++++++++++++++++++++ routers/controllers/vas.py | 104 +++++++ routers/controllers/webdav.py | 108 +++++++ routers/routers.py | 43 +++ test_main.py | 15 + 22 files changed, 2867 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 main.py create mode 100644 middleware/auth.py create mode 100644 models/model.py create mode 100644 models/response.py create mode 100644 pkg/JWT/jwt.py create mode 100644 pkg/conf/appmeta.py create mode 100644 routers/controllers/admin.py create mode 100644 routers/controllers/aria2.py create mode 100644 routers/controllers/callback.py create mode 100644 routers/controllers/directory.py create mode 100644 routers/controllers/file.py create mode 100644 routers/controllers/object.py create mode 100644 routers/controllers/share.py create mode 100644 routers/controllers/site.py create mode 100644 routers/controllers/tag.py create mode 100644 routers/controllers/user.py create mode 100644 routers/controllers/vas.py create mode 100644 routers/controllers/webdav.py create mode 100644 routers/routers.py create mode 100644 test_main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..267ca8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +__pycache__/ +.pytest_cache/ +.venv/ +.env/ +.vscode/ +.VSCodeCounter/ + +*.py[cod] +*.pyo +*.pyd + +*.code-workspace \ No newline at end of file diff --git a/README.md b/README.md index 929150e..8b8033a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ -# Server -DiskNext 服务端。支持多家云存储的公私兼备的网盘系统。 +

+
+ DiskNext Server +
+

+ +

支持多家云存储的公私兼备的云服务系统.

+ +本来此项目并没有这么快开始,但由于我曾经使用过的某个类似产品的作者吃相实在难看,故决定自己实现一个。 + +此项目的愿景是集百家之长(Cloudreve + Alist + FnOS)。你也可以考虑付费支持我们的发展 -> `DiskNext Pro`. + +目前正处于 `OMEGA` 实验阶段,比 `Alpha` 版还更早期,仅供测试。 + +## :alembic: 技术栈 + +* [Python ](https://www.python.org/) + [FastAPI](https://fastapi.tiangolo.com/) + + + +## :scroll: 许可证 + +GPL V3 + +--- +> GitHub [@Yuerchu](https://github.com/Yuerchu)  ·  +> Twitter [@LaBoyXiaoXin](https://twitter.com/LaBoyXiaoXin) diff --git a/main.py b/main.py new file mode 100644 index 0000000..84e0456 --- /dev/null +++ b/main.py @@ -0,0 +1,29 @@ +from fastapi import FastAPI +from routers import routers +from pkg.conf import appmeta + +app = FastAPI( + title=appmeta.APP_NAME, + summary=appmeta.summary, + description=appmeta.description, + version=appmeta.BackendVersion, + openapi_tags=appmeta.tags_meta, + license_info=appmeta.license_info, + +) + +for router in routers.Router: + app.include_router( + router, + prefix='/api', + responses={ + 200: {"description": "成功响应 Successful operation"}, + 401: {"description": "未授权 Unauthorized"}, + 403: {"description": "禁止访问 Forbidden"}, + 404: {"description": "未找到 Not found"}, + 500: {"description": "内部服务器错误 Internal server error"} + },) + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app='main:app', host="0.0.0.0", port=5213, reload=True) \ No newline at end of file diff --git a/middleware/auth.py b/middleware/auth.py new file mode 100644 index 0000000..69ac55c --- /dev/null +++ b/middleware/auth.py @@ -0,0 +1,30 @@ +from typing import Annotated, Literal +from fastapi import Depends +from pkg.JWT import jwt + +async def AuthRequired( + token: Annotated[str, Depends(jwt.oauth2_scheme)] +) -> Literal[True]: + ''' + AuthRequired 需要登录 + ''' + return True + +async def SignRequired( + token: Annotated[str, Depends(jwt.oauth2_scheme)] +) -> Literal[True]: + ''' + SignAuthRequired 需要登录并验证请求签名 + ''' + return True + +async def AdminRequired( + token: Annotated[str, Depends(jwt.oauth2_scheme)] +) -> Literal[True]: + ''' + 验证是否为管理员。 + + 使用方法: + >>> APIRouter(dependencies=[Depends(is_admin)]) + ''' + ... \ No newline at end of file diff --git a/models/model.py b/models/model.py new file mode 100644 index 0000000..cd6f7e2 --- /dev/null +++ b/models/model.py @@ -0,0 +1,215 @@ +from datetime import datetime +from sqlalchemy import Column, Integer, String, Text, BigInteger, Boolean, DateTime, ForeignKey +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + +class BaseModel(Base): + __abstract__ = True + + id = Column(Integer, primary_key=True, autoincrement=True) + created_at = Column(DateTime, default=datetime.now) + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + deleted_at = Column(DateTime, nullable=True) + +class Download(BaseModel): + __tablename__ = 'downloads' + + status = Column(Integer, nullable=True) + type = Column(Integer, nullable=True) + source = Column(Text, nullable=True) + total_size = Column(BigInteger, nullable=True) + downloaded_size = Column(BigInteger, nullable=True) + g_id = Column(Text, nullable=True) + speed = Column(Integer, nullable=True) + parent = Column(Text, nullable=True) + attrs = Column(Text, nullable=True) + error = Column(Text, nullable=True) + dst = Column(Text, nullable=True) + user_id = Column(Integer, nullable=True) + task_id = Column(Integer, nullable=True) + node_id = Column(Integer, nullable=True) + +class File(BaseModel): + __tablename__ = 'files' + + name = Column(String(255), nullable=True) + source_name = Column(Text, nullable=True) + user_id = Column(Integer, nullable=True) + size = Column(BigInteger, nullable=True) + pic_info = Column(String(255), nullable=True) + folder_id = Column(Integer, nullable=True) + policy_id = Column(Integer, nullable=True) + upload_session_id = Column(String(255), nullable=True, unique=True) + metadata = Column(Text, nullable=True) + +class Folder(BaseModel): + __tablename__ = 'folders' + + name = Column(String(255), nullable=True) + parent_id = Column(Integer, nullable=True, index=True) + owner_id = Column(Integer, nullable=True, index=True) + policy_id = Column(Integer, nullable=True) + + __table_args__ = {'uniqueConstraints': [('name', 'parent_id')]} + +class Group(BaseModel): + __tablename__ = 'groups' + + name = Column(String(255), nullable=True) + policies = Column(String(255), nullable=True) + max_storage = Column(BigInteger, nullable=True) + share_enabled = Column(Boolean, nullable=True) + web_dav_enabled = Column(Boolean, nullable=True) + speed_limit = Column(Integer, nullable=True) + options = Column(String(255), nullable=True) + +class Node(BaseModel): + __tablename__ = 'nodes' + + status = Column(Integer, nullable=True) + name = Column(String(255), nullable=True) + type = Column(Integer, nullable=True) + server = Column(String(255), nullable=True) + slave_key = Column(Text, nullable=True) + master_key = Column(Text, nullable=True) + aria2_enabled = Column(Boolean, nullable=True) + aria2_options = Column(Text, nullable=True) + rank = Column(Integer, nullable=True) + +class Order(BaseModel): + __tablename__ = 'orders' + + user_id = Column(Integer, nullable=True) + order_no = Column(String(255), nullable=True, index=True) + type = Column(Integer, nullable=True) + method = Column(String(255), nullable=True) + product_id = Column(BigInteger, nullable=True) + num = Column(Integer, nullable=True) + name = Column(String(255), nullable=True) + price = Column(Integer, nullable=True) + status = Column(Integer, nullable=True) + +class Policy(BaseModel): + __tablename__ = 'policies' + + name = Column(String(255), nullable=True) + type = Column(String(255), nullable=True) + server = Column(String(255), nullable=True) + bucket_name = Column(String(255), nullable=True) + is_private = Column(Boolean, nullable=True) + base_url = Column(String(255), nullable=True) + access_key = Column(Text, nullable=True) + secret_key = Column(Text, nullable=True) + max_size = Column(BigInteger, nullable=True) + auto_rename = Column(Boolean, nullable=True) + dir_name_rule = Column(String(255), nullable=True) + file_name_rule = Column(String(255), nullable=True) + is_origin_link_enable = Column(Boolean, nullable=True) + options = Column(Text, nullable=True) + +class Redeem(BaseModel): + __tablename__ = 'redeems' + + type = Column(Integer, nullable=True) + product_id = Column(BigInteger, nullable=True) + num = Column(Integer, nullable=True) + code = Column(Text, nullable=True) + used = Column(Boolean, nullable=True) + +class Report(BaseModel): + __tablename__ = 'reports' + + share_id = Column(Integer, nullable=True, index=True) + reason = Column(Integer, nullable=True) + description = Column(String(255), nullable=True) + +class Setting(BaseModel): + __tablename__ = 'settings' + + type = Column(String(255), nullable=False) + name = Column(String(255), nullable=False, unique=True, index=True) + value = Column(Text, nullable=True) + +class Share(BaseModel): + __tablename__ = 'shares' + + password = Column(String(255), nullable=True) + is_dir = Column(Boolean, nullable=True) + user_id = Column(Integer, nullable=True) + source_id = Column(Integer, nullable=True) + views = Column(Integer, nullable=True) + downloads = Column(Integer, nullable=True) + remain_downloads = Column(Integer, nullable=True) + expires = Column(DateTime, nullable=True) + preview_enabled = Column(Boolean, nullable=True) + source_name = Column(String(255), nullable=True, index=True) + score = Column(Integer, nullable=True) + +class SourceLink(BaseModel): + __tablename__ = 'source_links' + + file_id = Column(Integer, nullable=True) + name = Column(String(255), nullable=True) + downloads = Column(Integer, nullable=True) + +class StoragePack(BaseModel): + __tablename__ = 'storage_packs' + + name = Column(String(255), nullable=True) + user_id = Column(Integer, nullable=True) + active_time = Column(DateTime, nullable=True) + expired_time = Column(DateTime, nullable=True, index=True) + size = Column(BigInteger, nullable=True) + +class Tag(BaseModel): + __tablename__ = 'tags' + + name = Column(String(255), nullable=True) + icon = Column(String(255), nullable=True) + color = Column(String(255), nullable=True) + type = Column(Integer, nullable=True) + expression = Column(Text, nullable=True) + user_id = Column(Integer, nullable=True) + +class Task(BaseModel): + __tablename__ = 'tasks' + + status = Column(Integer, nullable=True) + type = Column(Integer, nullable=True) + user_id = Column(Integer, nullable=True) + progress = Column(Integer, nullable=True) + error = Column(Text, nullable=True) + props = Column(Text, nullable=True) + +class User(BaseModel): + __tablename__ = 'users' + + email = Column(String(100), nullable=True, unique=True) + nick = Column(String(50), nullable=True) + password = Column(String(255), nullable=True) + status = Column(Integer, nullable=True) + group_id = Column(Integer, nullable=True) + storage = Column(BigInteger, nullable=True) + two_factor = Column(String(255), nullable=True) + avatar = Column(String(255), nullable=True) + options = Column(Text, nullable=True) + authn = Column(Text, nullable=True) + open_id = Column(String(255), nullable=True) + score = Column(Integer, nullable=True) + previous_group_id = Column(Integer, nullable=True) + group_expires = Column(DateTime, nullable=True) + notify_date = Column(DateTime, nullable=True) + phone = Column(String(255), nullable=True) + +class WebDAV(BaseModel): + __tablename__ = 'webdavs' + + name = Column(String(255), nullable=True) + password = Column(String(255), nullable=True) + user_id = Column(Integer, nullable=True) + root = Column(Text, nullable=True) + readonly = Column(Boolean, nullable=True) + use_proxy = Column(Boolean, nullable=True) + + __table_args__ = {'uniqueConstraints': [('password', 'user_id')]} diff --git a/models/response.py b/models/response.py new file mode 100644 index 0000000..c00211d --- /dev/null +++ b/models/response.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel, Field +from typing import Union, Optional + +class ResponseModel(BaseModel): + code: int = Field(default=0, description="系统内部状态码, 0表示成功,其他表示失败", lt=60000, gt=0) + data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据") + msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示") \ No newline at end of file diff --git a/pkg/JWT/jwt.py b/pkg/JWT/jwt.py new file mode 100644 index 0000000..35dde07 --- /dev/null +++ b/pkg/JWT/jwt.py @@ -0,0 +1,9 @@ +from fastapi.security import OAuth2PasswordBearer + +oauth2_scheme = OAuth2PasswordBearer( + scheme_name='获取 JWT Bearer 令牌', + description='用于获取 JWT Bearer 令牌,需要以表单的形式提交', + tokenUrl="/api/user/session", + ) + +SECRET_KEY = '' \ No newline at end of file diff --git a/pkg/conf/appmeta.py b/pkg/conf/appmeta.py new file mode 100644 index 0000000..f0f10d6 --- /dev/null +++ b/pkg/conf/appmeta.py @@ -0,0 +1,103 @@ +APP_NAME = 'DiskNext Server' +summary = '一款基于 FastAPI 的可公私兼备的网盘系统' +description = 'DiskNext Server 是一款基于 FastAPI 的网盘系统,支持个人和企业使用。它提供了高性能的文件存储和管理功能,支持多种认证方式。' +license_info = {"name": "GPLv3", "url": "https://opensource.org/license/gpl-3.0"} + +BackendVersion = "0.0.1" + +IsPro = False + +tags_meta = [ + { + "name": "site", + "description": "站点", + }, + { + "name": "user", + "description": "用户", + }, + { + "name": "user_settings", + "description": "用户设置", + }, + { + "name": "share", + "description": "分享", + }, + { + "name": "file", + "description": "文件", + }, + { + "name": "aria2", + "description": "离线下载", + }, + { + "name": "directory", + "description": "目录", + }, + { + "name": "object", + "description": "对象,文件和目录的抽象", + }, + { + "name": "callback", + "description": "回调接口", + }, + { + "name": "oauth", + "description": "OAuth 认证", + }, + { + "name": "pay", + "description": "支付回调", + }, + { + "name": "upload", + "description": "上传回调", + }, + { + "name": "vas", + "description": "增值服务", + }, + { + "name": "tag", + "description": "用户标签", + }, + { + "name": "webdav", + "description": "WebDAV管理相关", + }, + { + "name": "admin", + "description": "管理员接口", + }, + { + "name": "admin_group", + "description": "管理员组接口", + }, + { + "name": "admin_user", + "description": "管理员用户接口", + }, + { + "name": "admin_file", + "description": "管理员文件接口", + }, + { + "name": "admin_aria2", + "description": "管理员离线下载接口", + }, + { + "name": "admin_policy", + "description": "管理员策略接口", + }, + { + "name": "admin_task", + "description": "管理员任务接口", + }, + { + "name": "admin_vas", + "description": "管理员增值服务接口", + } +] \ No newline at end of file diff --git a/routers/controllers/admin.py b/routers/controllers/admin.py new file mode 100644 index 0000000..cd91549 --- /dev/null +++ b/routers/controllers/admin.py @@ -0,0 +1,487 @@ +from fastapi import APIRouter, Depends +from middleware.auth import AdminRequired +from models.response import ResponseModel + +# 管理员根目录 /api/admin +admin_router = APIRouter( + prefix="/admin", + tags=["admin"], +) + +# 用户组 /api/admin/group +admin_group_router = APIRouter( + prefix="/admin/group", + tags=["admin", "admin_group"], +) + +# 用户 /api/admin/user +admin_user_router = APIRouter( + prefix="/admin/user", + tags=["admin", "admin_user"], +) + +# 文件 /api/admin/file +admin_file_router = APIRouter( + prefix="/admin/file", + tags=["admin", "admin_file"], +) + +# 离线下载 /api/admin/aria2 +admin_aria2_router = APIRouter( + prefix='/admin/aria2', + tags=['admin', 'admin_aria2'] +) + +# 存储策略管理 /api/admin/policy +admin_policy_router = APIRouter( + prefix='/admin/policy', + tags=['admin', 'admin_policy'] +) + +# 分享 /api/admin/share +admin_share_router = APIRouter( + prefix='/admin/share', + tags=['admin', 'admin_share'] +) + +# 任务 /api/admin/task +admin_task_router = APIRouter( + prefix='/admin/task', + tags=['admin', 'admin_task'] +) + +# 增值服务 /api/admin/vas +admin_vas_router = APIRouter( + prefix='/admin/vas', + tags=['admin', 'admin_vas'] +) + + +@admin_router.get( + path='/summary', + summary='获取站点概况', + description='Get site summary information', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_summary() -> ResponseModel: + """ + 获取站点概况信息,包括用户数、分享数、文件数等。 + + Returns: + ResponseModel: 包含站点概况信息的响应模型。 + """ + ... + +@admin_router.get( + path='/news', + summary='获取社区新闻', + description='Get community news', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_news() -> ResponseModel: + """ + 获取社区新闻信息,包括最新的动态和公告。 + + Returns: + ResponseModel: 包含社区新闻信息的响应模型。 + """ + ... + +@admin_router.patch( + path='/settings', + summary='更新设置', + description='Update settings', + dependencies=[Depends(AdminRequired)], +) +def router_admin_update_settings() -> ResponseModel: + """ + 更新站点设置,包括站点名称、描述等。 + + Returns: + ResponseModel: 包含更新结果的响应模型。 + """ + ... + +@admin_router.get( + path='/settings', + summary='获取设置', + description='Get settings', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_settings() -> ResponseModel: + """ + 获取站点设置,包括站点名称、描述等。 + + Returns: + ResponseModel: 包含站点设置的响应模型。 + """ + ... + +@admin_group_router.get( + path='/', + summary='获取用户组列表', + description='Get user group list', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_groups() -> ResponseModel: + """ + 获取用户组列表,包括每个用户组的名称和权限信息。 + + Returns: + ResponseModel: 包含用户组列表的响应模型。 + """ + ... + +@admin_group_router.get( + path='/{group_id}', + summary='获取用户组信息', + description='Get user group information by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_group(group_id: int) -> ResponseModel: + """ + 根据用户组ID获取用户组信息,包括名称、权限等。 + + Args: + group_id (int): 用户组ID。 + + Returns: + ResponseModel: 包含用户组信息的响应模型。 + """ + ... + +@admin_group_router.get( + path='/list/{group_id}', + summary='获取用户组成员列表', + description='Get user group member list by group ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_group_members( + group_id: int, + page: int = 1, + page_size: int = 20 +) -> ResponseModel: + """ + 根据用户组ID获取用户组成员列表。 + + Args: + group_id (int): 用户组ID。 + page (int): 页码,默认为1。 + page_size (int, optional): 每页显示的成员数量,默认为20。 + + Returns: + ResponseModel: 包含用户组成员列表的响应模型。 + """ + ... + +@admin_group_router.post( + path='/', + summary='创建用户组', + description='Create a new user group', + dependencies=[Depends(AdminRequired)], +) +def router_admin_create_group() -> ResponseModel: + """ + 创建一个新的用户组,设置名称和权限等信息。 + + Returns: + ResponseModel: 包含创建结果的响应模型。 + """ + ... + +@admin_group_router.patch( + path='/{group_id}', + summary='更新用户组信息', + description='Update user group information by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_update_group(group_id: int) -> ResponseModel: + """ + 根据用户组ID更新用户组信息,包括名称、权限等。 + + Args: + group_id (int): 用户组ID。 + + Returns: + ResponseModel: 包含更新结果的响应模型。 + """ + ... + +@admin_group_router.delete( + path='/{group_id}', + summary='删除用户组', + description='Delete user group by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_delete_group(group_id: int) -> ResponseModel: + """ + 根据用户组ID删除用户组。 + + Args: + group_id (int): 用户组ID。 + + Returns: + ResponseModel: 包含删除结果的响应模型。 + """ + ... + +@admin_user_router.get( + path='/info/{user_id}', + summary='获取用户信息', + description='Get user information by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_user(user_id: int) -> ResponseModel: + """ + 根据用户ID获取用户信息,包括用户名、邮箱、注册时间等。 + + Args: + user_id (int): 用户ID。 + + Returns: + ResponseModel: 包含用户信息的响应模型。 + """ + ... + +@admin_user_router.get( + path='/list', + summary='获取用户列表', + description='Get user list', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_users( + page: int = 1, + page_size: int = 20 +) -> ResponseModel: + """ + 获取用户列表,支持分页。 + + Args: + page (int): 页码,默认为1。 + page_size (int, optional): 每页显示的用户数量,默认为20。 + + Returns: + ResponseModel: 包含用户列表的响应模型。 + """ + ... + +@admin_user_router.post( + path='/create', + summary='创建用户', + description='Create a new user', + dependencies=[Depends(AdminRequired)], +) +def router_admin_create_user() -> ResponseModel: + """ + 创建一个新的用户,设置用户名、密码等信息。 + + Returns: + ResponseModel: 包含创建结果的响应模型。 + """ + ... + +@admin_user_router.patch( + path='/{user_id}', + summary='更新用户信息', + description='Update user information by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_update_user(user_id: int) -> ResponseModel: + """ + 根据用户ID更新用户信息,包括用户名、邮箱等。 + + Args: + user_id (int): 用户ID。 + + Returns: + ResponseModel: 包含更新结果的响应模型。 + """ + ... + +@admin_user_router.delete( + path='/{user_id}', + summary='删除用户', + description='Delete user by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_delete_user(user_id: int) -> ResponseModel: + """ + 根据用户ID删除用户。 + + Args: + user_id (int): 用户ID。 + + Returns: + ResponseModel: 包含删除结果的响应模型。 + """ + ... + +@admin_user_router.post( + path='/calibrate/{user_id}', + summary='校准用户存储容量', + description='Calibrate the user storage.', + dependencies=[Depends(AdminRequired)] +) +def router_admin_calibrate_storage(): + ... + +@admin_file_router.get( + path='/list', + summary='获取文件', + description='Get file list', + dependencies=[Depends(AdminRequired)], +) +def router_admin_get_file_list() -> ResponseModel: + """ + 获取文件列表,包括文件名称、大小、上传时间等。 + + Returns: + ResponseModel: 包含文件列表的响应模型。 + """ + ... + +@admin_file_router.get( + path='/preview/{file_id}', + summary='预览文件', + description='Preview file by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_preview_file(file_id: int) -> ResponseModel: + """ + 根据文件ID预览文件内容。 + + Args: + file_id (int): 文件ID。 + + Returns: + ResponseModel: 包含文件预览内容的响应模型。 + """ + ... + +@admin_file_router.patch( + path='/ban/{file_id}', + summary='封禁文件', + description='Ban the file, user can\'t open, copy, move, download or share this file if administrator ban.', + dependencies=[Depends(AdminRequired)], +) +def router_admin_ban_file(file_id: int) -> ResponseModel: + """ + 根据文件ID封禁文件。 + + 如果管理员封禁了某个文件,用户将无法打开、复制或移动、下载或分享此文件。 + + Args: + file_id (int): 文件ID。 + + Returns: + ResponseModel: 包含删除结果的响应模型。 + """ + ... + +@admin_file_router.delete( + path='/{file_id}', + summary='删除文件', + description='Delete file by ID', + dependencies=[Depends(AdminRequired)], +) +def router_admin_delete_file(file_id: int) -> ResponseModel: + """ + 根据文件ID删除文件。 + + Args: + file_id (int): 文件ID。 + + Returns: + ResponseModel: 包含删除结果的响应模型。 + """ + ... + +@admin_aria2_router.post( + path='/test', + summary='测试连接配置', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_admin_aira2_test() -> ResponseModel: + ... + +@admin_policy_router.get( + path='/list', + summary='列出存储策略', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_list() -> ResponseModel: + ... + +@admin_policy_router.post( + path='/test/path', + summary='测试本地路径可用性', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_test_path() -> ResponseModel: + ... + +@admin_policy_router.post( + path='/test/slave', + summary='测试从机通信', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_test_slave() -> ResponseModel: + ... + +@admin_policy_router.post( + path='/', + summary='创建存储策略', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_add_policy() -> ResponseModel: + ... + +@admin_policy_router.post( + path='/cors', + summary='创建跨域策略', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_add_cors() -> ResponseModel: + ... + +@admin_policy_router.post( + path='/scf', + summary='创建COS回调函数', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_add_scf() -> ResponseModel: + ... + +@admin_policy_router.get( + path='/{id}/oauth', + summary='获取 OneDrive OAuth URL', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_onddrive_oauth() -> ResponseModel: + ... + +@admin_policy_router.get( + path='/{id}', + summary='获取存储策略', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_get_policy() -> ResponseModel: + ... + +@admin_policy_router.delete( + path='/{id}', + summary='删除存储策略', + description='', + dependencies=[Depends(AdminRequired)] +) +def router_policy_delete_policy() -> ResponseModel: + ... \ No newline at end of file diff --git a/routers/controllers/aria2.py b/routers/controllers/aria2.py new file mode 100644 index 0000000..d9799c5 --- /dev/null +++ b/routers/controllers/aria2.py @@ -0,0 +1,107 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +aria2_router = APIRouter( + prefix="/aria2", + tags=["aria2"] +) + +@aria2_router.post( + path='/url', + summary='创建URL下载任务', + description='Create a URL download task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_url() -> ResponseModel: + """ + Create a URL download task endpoint. + + Returns: + ResponseModel: A model containing the response data for the URL download task. + """ + ... + +@aria2_router.post( + path='/torrent/{id}', + summary='创建种子下载任务', + description='Create a torrent download task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_torrent(id: str) -> ResponseModel: + """ + Create a torrent download task endpoint. + + Args: + id (str): The ID of the torrent to download. + + Returns: + ResponseModel: A model containing the response data for the torrent download task. + """ + ... + +@aria2_router.put( + path='/select/{gid}', + summary='重新选择要下载的文件', + description='Re-select files to download endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_select(gid: str) -> ResponseModel: + """ + Re-select files to download endpoint. + + Args: + gid (str): The GID of the download task. + + Returns: + ResponseModel: A model containing the response data for the re-selection of files. + """ + ... + +@aria2_router.delete( + path='/task/{gid}', + summary='取消或删除下载任务', + description='Delete a download task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_delete(gid: str) -> ResponseModel: + """ + Delete a download task endpoint. + + Args: + gid (str): The GID of the download task to delete. + + Returns: + ResponseModel: A model containing the response data for the deletion of the download task. + """ + ... + +@aria2_router.get( + '/downloading', + summary='获取正在下载中的任务', + description='Get currently downloading tasks endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_downloading() -> ResponseModel: + """ + Get currently downloading tasks endpoint. + + Returns: + ResponseModel: A model containing the response data for currently downloading tasks. + """ + ... + +@aria2_router.get( + path='/finished', + summary='获取已完成的任务', + description='Get finished tasks endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_aria2_finished() -> ResponseModel: + """ + Get finished tasks endpoint. + + Returns: + ResponseModel: A model containing the response data for finished tasks. + """ + ... \ No newline at end of file diff --git a/routers/controllers/callback.py b/routers/controllers/callback.py new file mode 100644 index 0000000..019f4c2 --- /dev/null +++ b/routers/controllers/callback.py @@ -0,0 +1,279 @@ +from fastapi import APIRouter, Depends +from fastapi.responses import PlainTextResponse +from middleware.auth import SignRequired +from models.response import ResponseModel + +callback_router = APIRouter( + prefix='/callback', + tags=["callback"], +) + +oauth_router = APIRouter( + prefix='/callback/oauth', + tags=["callback", "oauth"], +) + +pay_router = APIRouter( + prefix='/callback/pay', + tags=["callback", "pay"], +) + +upload_router = APIRouter( + prefix='/callback/upload', + tags=["callback", "upload"], +) + +callback_router.include_router(oauth_router) +callback_router.include_router(pay_router) +callback_router.include_router(upload_router) + +@oauth_router.post( + path='/qq', + summary='QQ互联回调', + description='Handle QQ OAuth callback and return user information.', +) +def router_callback_qq() -> ResponseModel: + """ + Handle QQ OAuth callback and return user information. + + Returns: + ResponseModel: A model containing the response data for the QQ OAuth callback. + """ + ... + +@oauth_router.post( + path='/github', + summary='GitHub OAuth 回调', + description='Handle GitHub OAuth callback and return user information.', +) +def router_callback_github() -> ResponseModel: + """ + Handle GitHub OAuth callback and return user information. + + Returns: + ResponseModel: A model containing the response data for the GitHub OAuth callback. + """ + ... + +@pay_router.post( + path='/alipay', + summary='支付宝支付回调', + description='Handle Alipay payment callback and return payment status.', +) +def router_callback_alipay() -> ResponseModel: + """ + Handle Alipay payment callback and return payment status. + + Returns: + ResponseModel: A model containing the response data for the Alipay payment callback. + """ + ... + +@pay_router.post( + path='/wechat', + summary='微信支付回调', + description='Handle WeChat Pay payment callback and return payment status.', +) +def router_callback_wechat() -> ResponseModel: + """ + Handle WeChat Pay payment callback and return payment status. + + Returns: + ResponseModel: A model containing the response data for the WeChat Pay payment callback. + """ + ... + +@pay_router.post( + path='/stripe', + summary='Stripe支付回调', + description='Handle Stripe payment callback and return payment status.', +) +def router_callback_stripe() -> ResponseModel: + """ + Handle Stripe payment callback and return payment status. + + Returns: + ResponseModel: A model containing the response data for the Stripe payment callback. + """ + ... + +@pay_router.get( + path='/easypay', + summary='易支付回调', + description='Handle EasyPay payment callback and return payment status.', +) +def router_callback_easypay() -> PlainTextResponse: + """ + Handle EasyPay payment callback and return payment status. + + Returns: + PlainTextResponse: A response containing the payment status for the EasyPay payment callback. + """ + ... + # return PlainTextResponse("success", status_code=200) + +@pay_router.get( + path='/custom/{order_no}/{id}', + summary='自定义支付回调', + description='Handle custom payment callback and return payment status.', +) +def router_callback_custom(order_no: str, id: str) -> ResponseModel: + """ + Handle custom payment callback and return payment status. + + Args: + order_no (str): The order number for the payment. + id (str): The ID associated with the payment. + + Returns: + ResponseModel: A model containing the response data for the custom payment callback. + """ + ... + +@upload_router.post( + path='/remote/{session_id}/{key}', + summary='远程上传回调', + description='Handle remote upload callback and return upload status.', +) +def router_callback_remote(session_id: str, key: str) -> ResponseModel: + """ + Handle remote upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + key (str): The key for the uploaded file. + + Returns: + ResponseModel: A model containing the response data for the remote upload callback. + """ + ... + +@upload_router.post( + path='/qiniu/{session_id}', + summary='七牛云上传回调', + description='Handle Qiniu Cloud upload callback and return upload status.', +) +def router_callback_qiniu(session_id: str) -> ResponseModel: + """ + Handle Qiniu Cloud upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the Qiniu Cloud upload callback. + """ + ... + +@upload_router.post( + path='/tencent/{session_id}', + summary='腾讯云上传回调', + description='Handle Tencent Cloud upload callback and return upload status.', +) +def router_callback_tencent(session_id: str) -> ResponseModel: + """ + Handle Tencent Cloud upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the Tencent Cloud upload callback. + """ + ... + +@upload_router.post( + path='/aliyun/{session_id}', + summary='阿里云上传回调', + description='Handle Aliyun upload callback and return upload status.', +) +def router_callback_aliyun(session_id: str) -> ResponseModel: + """ + Handle Aliyun upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the Aliyun upload callback. + """ + ... + +@upload_router.post( + path='/upyun/{session_id}', + summary='又拍云上传回调', + description='Handle Upyun upload callback and return upload status.', +) +def router_callback_upyun(session_id: str) -> ResponseModel: + """ + Handle Upyun upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the Upyun upload callback. + """ + ... + +@upload_router.post( + path='/aws/{session_id}', + summary='AWS S3上传回调', + description='Handle AWS S3 upload callback and return upload status.', +) +def router_callback_aws(session_id: str) -> ResponseModel: + """ + Handle AWS S3 upload callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the AWS S3 upload callback. + """ + ... + +@upload_router.post( + path='/onedrive/finish/{session_id}', + summary='OneDrive上传完成回调', + description='Handle OneDrive upload completion callback and return upload status.', +) +def router_callback_onedrive_finish(session_id: str) -> ResponseModel: + """ + Handle OneDrive upload completion callback and return upload status. + + Args: + session_id (str): The session ID for the upload. + + Returns: + ResponseModel: A model containing the response data for the OneDrive upload completion callback. + """ + ... + +@upload_router.get( + path='/ondrive/auth', + summary='OneDrive授权回调', + description='Handle OneDrive authorization callback and return authorization status.', +) +def router_callback_onedrive_auth() -> ResponseModel: + """ + Handle OneDrive authorization callback and return authorization status. + + Returns: + ResponseModel: A model containing the response data for the OneDrive authorization callback. + """ + ... + +@upload_router.get( + path='/google/auth', + summary='Google OAuth 完成', + description='Handle Google OAuth completion callback and return authorization status.', +) +def router_callback_google_auth() -> ResponseModel: + """ + Handle Google OAuth completion callback and return authorization status. + + Returns: + ResponseModel: A model containing the response data for the Google OAuth completion callback. + """ + ... \ No newline at end of file diff --git a/routers/controllers/directory.py b/routers/controllers/directory.py new file mode 100644 index 0000000..77e43f6 --- /dev/null +++ b/routers/controllers/directory.py @@ -0,0 +1,41 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +directory_router = APIRouter( + prefix="/directory", + tags=["directory"] +) + +@directory_router.put( + path='/', + summary='创建目录', + description='Create a directory endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_directory_create() -> ResponseModel: + """ + Create a directory endpoint. + + Returns: + ResponseModel: A model containing the response data for the directory creation. + """ + ... + +@directory_router.get( + path='/{path:path}', + summary='获取目录内容', + description='Get directory contents endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_directory_get(path: str) -> ResponseModel: + """ + Get directory contents endpoint. + + Args: + path (str): The path of the directory to retrieve contents from. + + Returns: + ResponseModel: A model containing the response data for the directory contents. + """ + ... \ No newline at end of file diff --git a/routers/controllers/file.py b/routers/controllers/file.py new file mode 100644 index 0000000..48c7178 --- /dev/null +++ b/routers/controllers/file.py @@ -0,0 +1,381 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +file_router = APIRouter( + prefix="/file", + tags=["file"] +) + +file_upload_router = APIRouter( + prefix="/file/upload", + tags=["file"] +) + +@file_router.get( + path='/get/{id}/{name}', + summary='文件外链(直接输出文件数据)', + description='Get file external link endpoint.', +) +def router_file_get(id: str, name: str) -> ResponseModel: + """ + Get file external link endpoint. + + Args: + id (str): The ID of the file. + name (str): The name of the file. + + Returns: + ResponseModel: A model containing the response data for the file. + """ + ... + +@file_router.get( + path='/source/{id}/{name}', + summary='文件外链(301跳转)', + description='Get file external link with 301 redirect endpoint.', +) +def router_file_source(id: str, name: str) -> ResponseModel: + """ + Get file external link with 301 redirect endpoint. + + Args: + id (str): The ID of the file. + name (str): The name of the file. + + Returns: + ResponseModel: A model containing the response data for the file with a redirect. + """ + ... + +@file_upload_router.get( + path='/download/{id}', + summary='下载文件', + description='Download file endpoint.', +) +def router_file_download(id: str) -> ResponseModel: + """ + Download file endpoint. + + Args: + id (str): The ID of the file to download. + + Returns: + ResponseModel: A model containing the response data for the file download. + """ + ... + +@file_upload_router.get( + path='/archive/{sessionID}/archive.zip', + summary='打包并下载文件', + description='Archive and download files endpoint.', +) +def router_file_archive_download(sessionID: str) -> ResponseModel: + """ + Archive and download files endpoint. + + Args: + sessionID (str): The session ID for the archive. + + Returns: + ResponseModel: A model containing the response data for the archived files download. + """ + ... + +@file_upload_router.post( + path='/{sessionID}/{index}', + summary='文件上传', + description='File upload endpoint.', +) +def router_file_upload(sessionID: str, index: int) -> ResponseModel: + """ + File upload endpoint. + + Args: + sessionID (str): The session ID for the upload. + index (int): The index of the file being uploaded. + + Returns: + ResponseModel: A model containing the response data. + """ + ... + +@file_upload_router.put( + path='/', + summary='创建上传会话', + description='Create an upload session endpoint.', + dependencies=[Depends(SignRequired)], +) +def router_file_upload_session() -> ResponseModel: + """ + Create an upload session endpoint. + + Returns: + ResponseModel: A model containing the response data for the upload session. + """ + ... + +@file_upload_router.delete( + path='/{sessionID}', + summary='删除上传会话', + description='Delete an upload session endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_upload_session_delete(sessionID: str) -> ResponseModel: + """ + Delete an upload session endpoint. + + Args: + sessionID (str): The session ID to delete. + + Returns: + ResponseModel: A model containing the response data for the deletion. + """ + ... + +@file_upload_router.delete( + path='/', + summary='清除所有上传会话', + description='Clear all upload sessions endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_upload_session_clear() -> ResponseModel: + """ + Clear all upload sessions endpoint. + + Returns: + ResponseModel: A model containing the response data for clearing all sessions. + """ + ... + +@file_router.put( + path='/update/{id}', + summary='更新文件', + description='Update file information endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_update(id: str) -> ResponseModel: + """ + Update file information endpoint. + + Args: + id (str): The ID of the file to update. + + Returns: + ResponseModel: A model containing the response data for the file update. + """ + ... + +@file_router.post( + path='/create', + summary='创建空白文件', + description='Create a blank file endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_create() -> ResponseModel: + """ + Create a blank file endpoint. + + Returns: + ResponseModel: A model containing the response data for the file creation. + """ + ... + +@file_router.put( + path='/download/{id}', + summary='创建文件下载会话', + description='Create a file download session endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_download(id: str) -> ResponseModel: + """ + Create a file download session endpoint. + + Args: + id (str): The ID of the file to download. + + Returns: + ResponseModel: A model containing the response data for the file download session. + """ + ... + +@file_router.get( + path='/preview/{id}', + summary='预览文件', + description='Preview file endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_preview(id: str) -> ResponseModel: + """ + Preview file endpoint. + + Args: + id (str): The ID of the file to preview. + + Returns: + ResponseModel: A model containing the response data for the file preview. + """ + ... + +@file_router.get( + path='/content/{id}', + summary='获取文本文件内容', + description='Get text file content endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_content(id: str) -> ResponseModel: + """ + Get text file content endpoint. + + Args: + id (str): The ID of the text file. + + Returns: + ResponseModel: A model containing the response data for the text file content. + """ + ... + +@file_router.get( + path='/doc/{id}', + summary='获取Office文档预览地址', + description='Get Office document preview URL endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_doc(id: str) -> ResponseModel: + """ + Get Office document preview URL endpoint. + + Args: + id (str): The ID of the Office document. + + Returns: + ResponseModel: A model containing the response data for the Office document preview URL. + """ + ... + +@file_router.get( + path='/thumb/{id}', + summary='获取文件缩略图', + description='Get file thumbnail endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_thumb(id: str) -> ResponseModel: + """ + Get file thumbnail endpoint. + + Args: + id (str): The ID of the file to get the thumbnail for. + + Returns: + ResponseModel: A model containing the response data for the file thumbnail. + """ + ... + +@file_router.post( + path='/source/{id}', + summary='取得文件外链', + description='Get file external link endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_source(id: str) -> ResponseModel: + """ + Get file external link endpoint. + + Args: + id (str): The ID of the file to get the external link for. + + Returns: + ResponseModel: A model containing the response data for the file external link. + """ + ... + +@file_router.post( + path='/archive', + summary='打包要下载的文件', + description='Archive files for download endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_archive(id: str) -> ResponseModel: + """ + Archive files for download endpoint. + + Args: + id (str): The ID of the file to archive. + + Returns: + ResponseModel: A model containing the response data for the archived files. + """ + ... + +@file_router.post( + path='/compress', + summary='创建文件压缩任务', + description='Create file compression task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_compress(id: str) -> ResponseModel: + """ + Create file compression task endpoint. + + Args: + id (str): The ID of the file to compress. + + Returns: + ResponseModel: A model containing the response data for the file compression task. + """ + ... + +@file_router.post( + path='/decompress', + summary='创建文件解压任务', + description='Create file extraction task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_decompress(id: str) -> ResponseModel: + """ + Create file extraction task endpoint. + + Args: + id (str): The ID of the file to decompress. + + Returns: + ResponseModel: A model containing the response data for the file extraction task. + """ + ... + +@file_router.post( + path='/relocate', + summary='创建文件转移任务', + description='Create file relocation task endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_relocate(id: str) -> ResponseModel: + """ + Create file relocation task endpoint. + + Args: + id (str): The ID of the file to relocate. + + Returns: + ResponseModel: A model containing the response data for the file relocation task. + """ + ... + +@file_router.get( + path='/search/{type}/{keyword}', + summary='搜索文件', + description='Search files by keyword endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_file_search(type: str, keyword: str) -> ResponseModel: + """ + Search files by keyword endpoint. + + Args: + type (str): The type of search (e.g., 'name', 'content'). + keyword (str): The keyword to search for. + + Returns: + ResponseModel: A model containing the response data for the file search. + """ + ... \ No newline at end of file diff --git a/routers/controllers/object.py b/routers/controllers/object.py new file mode 100644 index 0000000..0724c02 --- /dev/null +++ b/routers/controllers/object.py @@ -0,0 +1,86 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +object_router = APIRouter( + prefix="/object", + tags=["object"] +) + +@object_router.delete( + path='/', + summary='删除对象', + description='Delete an object endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_object_delete() -> ResponseModel: + """ + Delete an object endpoint. + + Returns: + ResponseModel: A model containing the response data for the object deletion. + """ + ... + +@object_router.patch( + path='/', + summary='移动对象', + description='Move an object endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_object_move() -> ResponseModel: + """ + Move an object endpoint. + + Returns: + ResponseModel: A model containing the response data for the object move. + """ + ... + +@object_router.post( + path='/copy', + summary='复制对象', + description='Copy an object endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_object_copy() -> ResponseModel: + """ + Copy an object endpoint. + + Returns: + ResponseModel: A model containing the response data for the object copy. + """ + ... + +@object_router.post( + path='/rename', + summary='重命名对象', + description='Rename an object endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_object_rename() -> ResponseModel: + """ + Rename an object endpoint. + + Returns: + ResponseModel: A model containing the response data for the object rename. + """ + ... + +@object_router.get( + path='/property/{id}', + summary='获取对象属性', + description='Get object properties endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_object_property(id: str) -> ResponseModel: + """ + Get object properties endpoint. + + Args: + id (str): The ID of the object to retrieve properties for. + + Returns: + ResponseModel: A model containing the response data for the object properties. + """ + ... \ No newline at end of file diff --git a/routers/controllers/share.py b/routers/controllers/share.py new file mode 100644 index 0000000..98b7ab1 --- /dev/null +++ b/routers/controllers/share.py @@ -0,0 +1,306 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +share_router = APIRouter( + prefix='/share', + tags=["share"], +) + +@share_router.get( + path='/{info}/{id}', + summary='获取分享', + description='Get shared content by info type and ID.', +) +def router_share_get(info: str, id: str) -> ResponseModel: + """ + Get shared content by info type and ID. + + Args: + info (str): The type of information being shared. + id (str): The ID of the shared content. + + Returns: + dict: A dictionary containing shared content information. + """ + ... + +@share_router.put( + path='/download/{id}', + summary='创建文件下载会话', + description='Create a file download session by ID.', +) +def router_share_download(id: str) -> ResponseModel: + """ + Create a file download session by ID. + + Args: + id (str): The ID of the file to be downloaded. + + Returns: + dict: A dictionary containing download session information. + """ + ... + +@share_router.get( + path='preview/{id}', + summary='预览分享文件', + description='Preview shared file by ID.', +) +def router_share_preview(id: str) -> ResponseModel: + """ + Preview shared file by ID. + + Args: + id (str): The ID of the file to be previewed. + + Returns: + dict: A dictionary containing preview information. + """ + ... + +@share_router.get( + path='/doc/{id}', + summary='取得Office文档预览地址', + description='Get Office document preview URL by ID.', +) +def router_share_doc(id: str) -> ResponseModel: + """ + Get Office document preview URL by ID. + + Args: + id (str): The ID of the Office document. + + Returns: + dict: A dictionary containing the document preview URL. + """ + ... + +@share_router.get( + path='/content/{id}', + summary='获取文本文件内容', + description='Get text file content by ID.', +) +def router_share_content(id: str) -> ResponseModel: + """ + Get text file content by ID. + + Args: + id (str): The ID of the text file. + + Returns: + str: The content of the text file. + """ + ... + +@share_router.get( + path='/list/{id}/{path:path}', + summary='获取目录列文件', + description='Get directory listing by ID and path.', +) +def router_share_list(id: str, path: str = '') -> ResponseModel: + """ + Get directory listing by ID and path. + + Args: + id (str): The ID of the directory. + path (str): The path within the directory. + + Returns: + dict: A dictionary containing directory listing information. + """ + ... + +@share_router.get( + path='/search/{id}/{type}/{keywords}', + summary='分享目录搜索', + description='Search within a shared directory by ID, type, and keywords.', +) +def router_share_search(id: str, type: str, keywords: str) -> ResponseModel: + """ + Search within a shared directory by ID, type, and keywords. + + Args: + id (str): The ID of the shared directory. + type (str): The type of search (e.g., file, folder). + keywords (str): The keywords to search for. + + Returns: + dict: A dictionary containing search results. + """ + ... + +@share_router.post( + path='/archive/{id}', + summary='归档打包下载', + description='Archive and download shared content by ID.', +) +def router_share_archive(id: str) -> ResponseModel: + """ + Archive and download shared content by ID. + + Args: + id (str): The ID of the content to be archived. + + Returns: + dict: A dictionary containing archive download information. + """ + ... + +@share_router.get( + path='/readme/{id}', + summary='获取README文本文件内容', + description='Get README text file content by ID.', +) +def router_share_readme(id: str) -> ResponseModel: + """ + Get README text file content by ID. + + Args: + id (str): The ID of the README file. + + Returns: + str: The content of the README file. + """ + ... + +@share_router.get( + path='/thumb/{id}/{file}', + summary='获取缩略图', + description='Get thumbnail image by ID and file name.', +) +def router_share_thumb(id: str, file: str) -> ResponseModel: + """ + Get thumbnail image by ID and file name. + + Args: + id (str): The ID of the shared content. + file (str): The name of the file for which to get the thumbnail. + + Returns: + str: A Base64 encoded string of the thumbnail image. + """ + ... + +@share_router.post( + path='/report/{id}', + summary='举报分享', + description='Report shared content by ID.', +) +def router_share_report(id: str) -> ResponseModel: + """ + Report shared content by ID. + + Args: + id (str): The ID of the shared content to report. + + Returns: + dict: A dictionary containing report submission information. + """ + ... + +@share_router.get( + path='/search', + summary='搜索公共分享', + description='Search public shares by keywords and type.', +) +def router_share_search_public(keywords: str, type: str = 'all') -> ResponseModel: + """ + Search public shares by keywords and type. + + Args: + keywords (str): The keywords to search for. + type (str): The type of search (e.g., all, file, folder). + + Returns: + dict: A dictionary containing search results for public shares. + """ + ... + +##################### +# 需要登录的接口 +##################### + +@share_router.post( + path='/', + summary='创建新分享', + description='Create a new share endpoint.', + dependencies=[Depends(SignRequired)] +) +def router_share_create() -> ResponseModel: + """ + Create a new share endpoint. + + Returns: + ResponseModel: A model containing the response data for the new share creation. + """ + ... + +@share_router.get( + path='/', + summary='列出我的分享', + description='Get a list of shares.', + dependencies=[Depends(SignRequired)] +) +def router_share_list() -> ResponseModel: + """ + Get a list of shares. + + Returns: + ResponseModel: A model containing the response data for the list of shares. + """ + ... + +@share_router.post( + path='/save/{id}', + summary='转存他人分享', + description='Save another user\'s share by ID.', + dependencies=[Depends(SignRequired)] +) +def router_share_save(id: str) -> ResponseModel: + """ + Save another user's share by ID. + + Args: + id (str): The ID of the share to be saved. + + Returns: + ResponseModel: A model containing the response data for the saved share. + """ + ... + +@share_router.patch( + path='/{id}', + summary='更新分享信息', + description='Update share information by ID.', + dependencies=[Depends(SignRequired)] +) +def router_share_update(id: str) -> ResponseModel: + """ + Update share information by ID. + + Args: + id (str): The ID of the share to be updated. + + Returns: + ResponseModel: A model containing the response data for the updated share. + """ + ... + +@share_router.delete( + path='/{id}', + summary='删除分享', + description='Delete a share by ID.', + dependencies=[Depends(SignRequired)] +) +def router_share_delete(id: str) -> ResponseModel: + """ + Delete a share by ID. + + Args: + id (str): The ID of the share to be deleted. + + Returns: + ResponseModel: A model containing the response data for the deleted share. + """ + ... \ No newline at end of file diff --git a/routers/controllers/site.py b/routers/controllers/site.py new file mode 100644 index 0000000..8dd8f5d --- /dev/null +++ b/routers/controllers/site.py @@ -0,0 +1,52 @@ +from fastapi import APIRouter +from models.response import ResponseModel + +site_router = APIRouter( + prefix="/site", + tags=["site"], +) + +@site_router.get( + path="/ping", + summary="测试用路由", + description="A simple endpoint to check if the site is up and running.", + response_model=ResponseModel,) +def router_site_ping(): + """ + Ping the site to check if it is up and running. + + Returns: + str: A message indicating the site is running. + """ + from pkg.conf.appmeta import BackendVersion + return ResponseModel(data=BackendVersion) + +@site_router.get( + path='/captcha', + summary='验证码', + description='Get a Base64 captcha image.', + response_model=ResponseModel, +) +def router_site_captcha(): + """ + Get a Base64 captcha image. + + Returns: + str: A Base64 encoded string of the captcha image. + """ + ... + +@site_router.get( + path='/config', + summary='站点全局配置', + description='Get the configuration file.', + response_model=ResponseModel, +) +def router_site_config(): + """ + Get the configuration file. + + Returns: + dict: The site configuration. + """ + ... \ No newline at end of file diff --git a/routers/controllers/tag.py b/routers/controllers/tag.py new file mode 100644 index 0000000..650d2d4 --- /dev/null +++ b/routers/controllers/tag.py @@ -0,0 +1,56 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +tag_router = APIRouter( + prefix='/tag', + tags=["tag"], +) + +@tag_router.post( + path='/filter', + summary='创建文件分类标签', + description='Create a file classification tag.', + dependencies=[Depends(SignRequired)], +) +def router_tag_create_filter() -> ResponseModel: + """ + Create a file classification tag. + + Returns: + ResponseModel: A model containing the response data for the created tag. + """ + ... + +@tag_router.post( + path='/link', + summary='创建目录快捷方式标签', + description='Create a directory shortcut tag.', + dependencies=[Depends(SignRequired)], +) +def router_tag_create_link() -> ResponseModel: + """ + Create a directory shortcut tag. + + Returns: + ResponseModel: A model containing the response data for the created tag. + """ + ... + +@tag_router.delete( + path='/{id}', + summary='删除标签', + description='Delete a tag by its ID.', + dependencies=[Depends(SignRequired)], +) +def router_tag_delete(id: str) -> ResponseModel: + """ + Delete a tag by its ID. + + Args: + id (str): The ID of the tag to be deleted. + + Returns: + ResponseModel: A model containing the response data for the deletion operation. + """ + ... \ No newline at end of file diff --git a/routers/controllers/user.py b/routers/controllers/user.py new file mode 100644 index 0000000..81a5f06 --- /dev/null +++ b/routers/controllers/user.py @@ -0,0 +1,370 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +user_router = APIRouter( + prefix="/user", + tags=["user"], +) + +user_settings_router = APIRouter( + prefix='/user/settings', + tags=["user", "user_settings"], + dependencies=[Depends(SignRequired)], +) + +@user_router.post( + path='/session', + summary='用户登录', + description='User login endpoint.', +) +def router_user_session() -> ResponseModel: + """ + User login endpoint. + + Returns: + dict: A dictionary containing user session information. + """ + ... + +@user_router.post( + path='/', + summary='用户注册', + description='User registration endpoint.', +) +def router_user_register() -> ResponseModel: + """ + User registration endpoint. + + Returns: + dict: A dictionary containing user registration information. + """ + ... + +@user_router.post( + path='/2fa', + summary='用两步验证登录', + description='Two-factor authentication login endpoint.', +) +def router_user_2fa() -> ResponseModel: + """ + Two-factor authentication login endpoint. + + Returns: + dict: A dictionary containing two-factor authentication information. + """ + ... + +@user_router.post( + path='/reset', + summary='发送密码重设邮件', + description='Send a password reset email.', +) +def router_user_reset() -> ResponseModel: + """ + Send a password reset email. + + Returns: + dict: A dictionary containing information about the password reset email. + """ + ... + +@user_router.patch( + path='/reset', + summary='通过邮件里的链接重设密码', + description='Reset password via email link.', +) +def router_user_reset_patch() -> ResponseModel: + """ + Reset password via email link. + + Returns: + dict: A dictionary containing information about the password reset. + """ + ... + +@user_router.get( + path='/qq', + summary='初始化QQ登录', + description='Initialize QQ login for a user.', +) +def router_user_qq() -> ResponseModel: + """ + Initialize QQ login for a user. + + Returns: + dict: A dictionary containing QQ login initialization information. + """ + ... + +@user_router.get( + path='/activate/{id}', + summary='邮件激活', + description='Activate user account via email link.', +) +def router_user_activate(id: str) -> ResponseModel: + """ + Activate user account via email link. + + Args: + id (str): The activation ID from the email link. + + Returns: + dict: A dictionary containing activation information. + """ + ... + +@user_router.get( + path='authn/{username}', + summary='WebAuthn登录初始化', + description='Initialize WebAuthn login for a user.', +) +def router_user_authn(username: str) -> ResponseModel: + """ + Initialize WebAuthn login for a user. + + Args: + username (str): The username of the user. + + Returns: + dict: A dictionary containing WebAuthn initialization information. + """ + ... + +@user_router.post( + path='authn/finish/{username}', + summary='WebAuthn登录', + description='Finish WebAuthn login for a user.', +) +def router_user_authn_finish(username: str) -> ResponseModel: + """ + Finish WebAuthn login for a user. + + Args: + username (str): The username of the user. + + Returns: + dict: A dictionary containing WebAuthn login information. + """ + ... + +@user_router.get( + path='/profile/{id}', + summary='获取用户主页展示用分享', + description='Get user profile for display.', +) +def router_user_profile(id: str) -> ResponseModel: + """ + Get user profile for display. + + Args: + id (str): The user ID. + + Returns: + dict: A dictionary containing user profile information. + """ + ... + +@user_router.get( + path='/avatar/{id}/{size}', + summary='获取用户头像', + description='Get user avatar by ID and size.', +) +def router_user_avatar(id: str, size: int = 128) -> ResponseModel: + """ + Get user avatar by ID and size. + + Args: + id (str): The user ID. + size (int): The size of the avatar image. + + Returns: + str: A Base64 encoded string of the user avatar image. + """ + ... + +##################### +# 需要登录的接口 +##################### + +@user_router.get( + path='/me', + summary='获取用户信息', + description='Get user information.', + dependencies=[Depends(SignRequired)], +) +def router_user_me() -> ResponseModel: + """ + Get user information. + + Returns: + dict: A dictionary containing user information. + """ + ... + +@user_router.get( + path='/storage', + summary='存储信息', + description='Get user storage information.', + dependencies=[Depends(SignRequired)], +) +def router_user_storage() -> ResponseModel: + """ + Get user storage information. + + Returns: + dict: A dictionary containing user storage information. + """ + ... + +@user_router.put( + path='/authn/start', + summary='WebAuthn登录初始化', + description='Initialize WebAuthn login for a user.', + dependencies=[Depends(SignRequired)], +) +def router_user_authn_start() -> ResponseModel: + """ + Initialize WebAuthn login for a user. + + Returns: + dict: A dictionary containing WebAuthn initialization information. + """ + ... + +@user_router.put( + path='/authn/finish', + summary='WebAuthn登录', + description='Finish WebAuthn login for a user.', + dependencies=[Depends(SignRequired)], +) +def router_user_authn_finish() -> ResponseModel: + """ + Finish WebAuthn login for a user. + + Returns: + dict: A dictionary containing WebAuthn login information. + """ + ... + +@user_settings_router.get( + path='/policies', + summary='获取用户可选存储策略', + description='Get user selectable storage policies.', +) +def router_user_settings_policies() -> ResponseModel: + """ + Get user selectable storage policies. + + Returns: + dict: A dictionary containing available storage policies for the user. + """ + ... + +@user_settings_router.get( + path='/nodes', + summary='获取用户可选节点', + description='Get user selectable nodes.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_nodes() -> ResponseModel: + """ + Get user selectable nodes. + + Returns: + dict: A dictionary containing available nodes for the user. + """ + ... + +@user_settings_router.get( + path='/tasks', + summary='任务队列', + description='Get user task queue.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_tasks() -> ResponseModel: + """ + Get user task queue. + + Returns: + dict: A dictionary containing the user's task queue information. + """ + ... + +@user_settings_router.get( + path='/', + summary='获取当前用户设定', + description='Get current user settings.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings() -> ResponseModel: + """ + Get current user settings. + + Returns: + dict: A dictionary containing the user's current settings. + """ + ... + +@user_settings_router.post( + path='/avatar', + summary='从文件上传头像', + description='Upload user avatar from file.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_avatar() -> ResponseModel: + """ + Upload user avatar from file. + + Returns: + dict: A dictionary containing the result of the avatar upload. + """ + ... + +@user_settings_router.put( + path='/avatar', + summary='设定为Gravatar头像', + description='Set user avatar to Gravatar.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_avatar_gravatar() -> ResponseModel: + """ + Set user avatar to Gravatar. + + Returns: + dict: A dictionary containing the result of setting the Gravatar avatar. + """ + ... + +@user_settings_router.patch( + path='/{option}', + summary='更新用户设定', + description='Update user settings.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_patch(option: str) -> ResponseModel: + """ + Update user settings. + + Args: + option (str): The setting option to update. + + Returns: + dict: A dictionary containing the result of the settings update. + """ + ... + +@user_settings_router.get( + path='/2fa', + summary='获取两步验证初始化信息', + description='Get two-factor authentication initialization information.', + dependencies=[Depends(SignRequired)], +) +def router_user_settings_2fa() -> ResponseModel: + """ + Get two-factor authentication initialization information. + + Returns: + dict: A dictionary containing two-factor authentication setup information. + """ + ... \ No newline at end of file diff --git a/routers/controllers/vas.py b/routers/controllers/vas.py new file mode 100644 index 0000000..8e98569 --- /dev/null +++ b/routers/controllers/vas.py @@ -0,0 +1,104 @@ +from fastapi import APIRouter, Depends +from middleware.auth import SignRequired +from models.response import ResponseModel + +vas_router = APIRouter( + prefix="/vas", + tags=["vas"] +) + +@vas_router.get( + path='/pack', + summary='获取容量包及配额信息', + description='Get information about storage packs and quotas.', + dependencies=[Depends(SignRequired)] +) +def router_vas_pack() -> ResponseModel: + """ + Get information about storage packs and quotas. + + Returns: + ResponseModel: A model containing the response data for storage packs and quotas. + """ + ... + +@vas_router.get( + path='/product', + summary='获取商品信息,同时返回支付信息', + description='Get product information along with payment details.', + dependencies=[Depends(SignRequired)] +) +def router_vas_product() -> ResponseModel: + """ + Get product information along with payment details. + + Returns: + ResponseModel: A model containing the response data for products and payment information. + """ + ... + +@vas_router.post( + path='/order', + summary='新建支付订单', + description='Create an order for a product.', + dependencies=[Depends(SignRequired)] +) +def router_vas_order() -> ResponseModel: + """ + Create an order for a product. + + Returns: + ResponseModel: A model containing the response data for the created order. + """ + ... + +@vas_router.get( + path='/order/{id}', + summary='查询订单状态', + description='Get information about a specific payment order by ID.', + dependencies=[Depends(SignRequired)] +) +def router_vas_order_get(id: str) -> ResponseModel: + """ + Get information about a specific payment order by ID. + + Args: + id (str): The ID of the order to retrieve information for. + + Returns: + ResponseModel: A model containing the response data for the specified order. + """ + ... + +@vas_router.get( + path='/redeem', + summary='获取兑换码信息', + description='Get information about a specific redemption code.', + dependencies=[Depends(SignRequired)] +) +def router_vas_redeem(code: str) -> ResponseModel: + """ + Get information about a specific redemption code. + + Args: + code (str): The redemption code to retrieve information for. + + Returns: + ResponseModel: A model containing the response data for the specified redemption code. + """ + ... + +@vas_router.post( + path='/redeem', + summary='执行兑换', + description='Redeem a redemption code for a product or service.', + dependencies=[Depends(SignRequired)] +) +def router_vas_redeem_post() -> ResponseModel: + """ + Redeem a redemption code for a product or service. + + Returns: + ResponseModel: A model containing the response data for the redeemed code. + """ + ... \ No newline at end of file diff --git a/routers/controllers/webdav.py b/routers/controllers/webdav.py new file mode 100644 index 0000000..489a6e7 --- /dev/null +++ b/routers/controllers/webdav.py @@ -0,0 +1,108 @@ +from fastapi import APIRouter, Depends, Request +from middleware.auth import SignRequired +from models.response import ResponseModel + +# WebDAV 管理路由 +webdav_router = APIRouter( + prefix='/webdav', + tags=["webdav"], +) + +@webdav_router.get( + path='/accounts', + summary='获取账号信息', + description='Get account information for WebDAV.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_accounts() -> ResponseModel: + """ + Get account information for WebDAV. + + Returns: + ResponseModel: A model containing the response data for the account information. + """ + ... + +@webdav_router.post( + path='/accounts', + summary='新建账号', + description='Create a new WebDAV account.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_create_account() -> ResponseModel: + """ + Create a new WebDAV account. + + Returns: + ResponseModel: A model containing the response data for the created account. + """ + ... + +@webdav_router.delete( + path='/accounts/{id}', + summary='删除账号', + description='Delete a WebDAV account by its ID.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_delete_account(id: str) -> ResponseModel: + """ + Delete a WebDAV account by its ID. + + Args: + id (str): The ID of the account to be deleted. + + Returns: + ResponseModel: A model containing the response data for the deletion operation. + """ + ... + +@webdav_router.post( + path='/mount', + summary='新建目录挂载', + description='Create a new WebDAV mount point.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_create_mount() -> ResponseModel: + """ + Create a new WebDAV mount point. + + Returns: + ResponseModel: A model containing the response data for the created mount point. + """ + ... + +@webdav_router.delete( + path='/mount/{id}', + summary='删除目录挂载', + description='Delete a WebDAV mount point by its ID.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_delete_mount(id: str) -> ResponseModel: + """ + Delete a WebDAV mount point by its ID. + + Args: + id (str): The ID of the mount point to be deleted. + + Returns: + ResponseModel: A model containing the response data for the deletion operation. + """ + ... + +@webdav_router.patch( + path='accounts/{id}', + summary='更新账号信息', + description='Update WebDAV account information by ID.', + dependencies=[Depends(SignRequired)], +) +def router_webdav_update_account(id: str) -> ResponseModel: + """ + Update WebDAV account information by ID. + + Args: + id (str): The ID of the account to be updated. + + Returns: + ResponseModel: A model containing the response data for the updated account. + """ + ... \ No newline at end of file diff --git a/routers/routers.py b/routers/routers.py new file mode 100644 index 0000000..8e180d0 --- /dev/null +++ b/routers/routers.py @@ -0,0 +1,43 @@ +from fastapi import APIRouter + +from .controllers import ( + share, + site, + user, + file, + aria2, + directory, + object, + callback, + vas, + tag, + webdav, + admin +) + +Router: list[APIRouter] = [ + share.share_router, + site.site_router, + user.user_router, + user.user_settings_router, + file.file_router, + file.file_upload_router, + aria2.aria2_router, + directory.directory_router, + object.object_router, + callback.callback_router, + callback.oauth_router, + callback.pay_router, + callback.upload_router, + vas.vas_router, + tag.tag_router, + webdav.webdav_router, + admin.admin_router, + admin.admin_group_router, + admin.admin_user_router, + admin.admin_file_router, + admin.admin_aria2_router, + admin.admin_policy_router, + admin.admin_task_router, + admin.admin_vas_router +] \ No newline at end of file diff --git a/test_main.py b/test_main.py new file mode 100644 index 0000000..074f5fc --- /dev/null +++ b/test_main.py @@ -0,0 +1,15 @@ +from fastapi.testclient import TestClient + +from main import app + +client = TestClient(app) + + +def test_read_main(): + from pkg.conf.appmeta import BackendVersion + response = client.get("/api/site/ping") + assert response.status_code == 200 + assert response.json() == { + "code": 0, + 'data': BackendVersion, + 'msg': None} \ No newline at end of file