From 51b6de921bdd342b642f04fc76387d1f3f2e363a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E5=B0=8F=E4=B8=98?= Date: Fri, 19 Dec 2025 18:04:34 +0800 Subject: [PATCH] feat: Implement API routers for user, tag, vas, webdav, and slave functionalities - Added user authentication and registration endpoints with JWT support. - Created tag management routes for creating and deleting tags. - Implemented value-added service (VAS) endpoints for managing storage packs and orders. - Developed WebDAV account management routes for creating, updating, and deleting accounts. - Introduced slave router for handling file uploads, downloads, and aria2 task management. - Enhanced JWT utility functions for token creation and secret key management. - Established lifespan management for FastAPI application startup and shutdown processes. - Integrated password handling utilities with Argon2 hashing and two-factor authentication support. --- main.py | 6 +- middleware/auth.py | 2 +- models/database.py | 2 +- models/group.py | 14 +- models/migration.py | 27 +- models/policy.py | 20 +- models/response.py | 11 +- .../admin.py => api/v1/admin/__init__.py} | 68 ++-- .../v1/callback/__init__.py} | 30 +- .../v1/directory/__init__.py} | 4 +- .../aria2.py => api/v1/download/__init__.py} | 14 +- .../file.py => api/v1/file/__init__.py} | 42 +-- .../object.py => api/v1/object/__init__.py} | 16 +- .../site.py => api/v1/site/__init__.py} | 14 +- .../slave.py => api/v1/slave/__init__.py} | 26 +- .../tag.py => api/v1/tag/__init__.py} | 8 +- .../user.py => api/v1/user/__init__.py} | 60 ++-- .../vas.py => api/v1/vas/__init__.py} | 14 +- .../webdav.py => api/v1/webdav/__init__.py} | 14 +- routers/controllers/share.py | 306 ------------------ routers/dav/README.md | 1 + routers/routers.py | 46 --- service/user/login.py | 4 +- tests/test_main.py | 2 +- tests/test_pkg_password.py | 2 +- pkg/JWT/jwt.py => utils/JWT/JWT.py | 0 {pkg => utils}/__init__.py | 0 {pkg => utils}/conf/appmeta.py | 0 {pkg => utils}/lifespan/lifespan.py | 0 {pkg => utils}/password/pwd.py | 4 +- 30 files changed, 223 insertions(+), 534 deletions(-) rename routers/{controllers/admin.py => api/v1/admin/__init__.py} (87%) rename routers/{controllers/callback.py => api/v1/callback/__init__.py} (92%) rename routers/{controllers/directory.py => api/v1/directory/__init__.py} (98%) rename routers/{controllers/aria2.py => api/v1/download/__init__.py} (88%) rename routers/{controllers/file.py => api/v1/file/__init__.py} (89%) rename routers/{controllers/object.py => api/v1/object/__init__.py} (93%) rename routers/{controllers/site.py => api/v1/site/__init__.py} (92%) rename routers/{controllers/slave.py => api/v1/slave/__init__.py} (90%) rename routers/{controllers/tag.py => api/v1/tag/__init__.py} (86%) rename routers/{controllers/user.py => api/v1/user/__init__.py} (91%) rename routers/{controllers/vas.py => api/v1/vas/__init__.py} (88%) rename routers/{controllers/webdav.py => api/v1/webdav/__init__.py} (86%) delete mode 100644 routers/controllers/share.py create mode 100644 routers/dav/README.md delete mode 100644 routers/routers.py rename pkg/JWT/jwt.py => utils/JWT/JWT.py (100%) rename {pkg => utils}/__init__.py (100%) rename {pkg => utils}/conf/appmeta.py (100%) rename {pkg => utils}/lifespan/lifespan.py (100%) rename {pkg => utils}/password/pwd.py (98%) diff --git a/main.py b/main.py index 288014a..376493a 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,10 @@ from fastapi import FastAPI -from pkg.conf import appmeta -from pkg.lifespan import lifespan +from utils.conf import appmeta +from utils.lifespan import lifespan from models.database import init_db from models.migration import migration -from pkg.JWT import JWT +from utils.JWT import JWT from routers import routers # 添加初始化数据库启动项 diff --git a/middleware/auth.py b/middleware/auth.py index 075da22..12b974e 100644 --- a/middleware/auth.py +++ b/middleware/auth.py @@ -5,7 +5,7 @@ from jwt import InvalidTokenError import jwt from models.user import User -from pkg.JWT import JWT +from utils.JWT import JWT from .dependencies import SessionDep credentials_exception = HTTPException( diff --git a/models/database.py b/models/database.py index 7dd0cc1..1600837 100644 --- a/models/database.py +++ b/models/database.py @@ -1,7 +1,7 @@ from sqlmodel import SQLModel from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine from sqlmodel.ext.asyncio.session import AsyncSession -from pkg.conf import appmeta +from utils.conf import appmeta from sqlalchemy.orm import sessionmaker from typing import AsyncGenerator diff --git a/models/group.py b/models/group.py index 8297f5b..4563c51 100644 --- a/models/group.py +++ b/models/group.py @@ -8,6 +8,7 @@ from .base import TableBase, SQLModelBase, UUIDTableBase if TYPE_CHECKING: from .user import User + from .policy import Policy # ==================== Base 模型 ==================== @@ -70,6 +71,10 @@ class GroupResponse(GroupBase, GroupOptionsBase): # ==================== 数据库模型 ==================== +# GroupPolicyLink 定义在 policy.py 中以避免循环导入 +from .policy import GroupPolicyLink + + class GroupOptions(GroupOptionsBase, TableBase, table=True): """用户组选项模型""" @@ -104,9 +109,6 @@ class Group(GroupBase, UUIDTableBase, table=True): name: str = Field(max_length=255, unique=True) """用户组名""" - policies: str | None = Field(default=None, max_length=255) - """允许的策略ID列表,逗号分隔""" - max_storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"}) """最大存储空间(字节)""" @@ -128,6 +130,12 @@ class Group(GroupBase, UUIDTableBase, table=True): sa_relationship_kwargs={"uselist": False} ) + # 多对多关系:用户组可以关联多个存储策略 + policies: list["Policy"] = Relationship( + back_populates="groups", + link_model=GroupPolicyLink, + ) + # 关系:一个组可以有多个用户 user: list["User"] = Relationship( back_populates="group", diff --git a/models/migration.py b/models/migration.py index 2f22c33..bd08020 100644 --- a/models/migration.py +++ b/models/migration.py @@ -1,8 +1,8 @@ from .setting import Setting, SettingsType from .color import ThemeResponse -from pkg.conf.appmeta import BackendVersion -from pkg.password.pwd import Password +from utils.conf.appmeta import BackendVersion +from utils.password.pwd import Password from loguru import logger as log async def migration() -> None: @@ -138,12 +138,17 @@ async def init_default_settings() -> None: async def init_default_group() -> None: from .group import Group, GroupOptions + from .policy import Policy, GroupPolicyLink from .setting import Setting from .database import get_session log.info('初始化用户组...') async for session in get_session(): + # 获取默认存储策略 + default_policy = await Policy.get(session, Policy.name == "本地存储") + default_policy_id = default_policy.id if default_policy else None + # 未找到初始管理组时,则创建 if not await Group.get(session, Group.name == "管理员"): admin_group = Group( @@ -167,6 +172,14 @@ async def init_default_group() -> None: advance_delete=True, ).save(session) + # 关联默认存储策略 + if default_policy_id: + session.add(GroupPolicyLink( + group_id=admin_group_id, + policy_id=default_policy_id, + )) + await session.commit() + # 未找到初始注册会员时,则创建 if not await Group.get(session, Group.name == "注册会员"): member_group = Group( @@ -183,6 +196,14 @@ async def init_default_group() -> None: share_download=True, ).save(session) + # 关联默认存储策略 + if default_policy_id: + session.add(GroupPolicyLink( + group_id=member_group_id, + policy_id=default_policy_id, + )) + await session.commit() + # 更新 default_group 设置为注册会员组的 UUID default_group_setting = await Setting.get(session, Setting.name == "default_group") if default_group_setting: @@ -204,6 +225,8 @@ async def init_default_group() -> None: share_download=True, ).save(session) + # 游客组不关联存储策略(无法上传) + async def init_default_user() -> None: from .user import User from .group import Group diff --git a/models/policy.py b/models/policy.py index 9ed8319..7b0bbdb 100644 --- a/models/policy.py +++ b/models/policy.py @@ -1,12 +1,24 @@ from typing import TYPE_CHECKING +from uuid import UUID from enum import StrEnum from sqlmodel import Field, Relationship, text -from .base import UUIDTableBase +from .base import SQLModelBase, UUIDTableBase if TYPE_CHECKING: from .object import Object + from .group import Group + + +class GroupPolicyLink(SQLModelBase, table=True): + """用户组与存储策略的多对多关联表""" + + group_id: UUID = Field(foreign_key="group.id", primary_key=True) + """用户组UUID""" + + policy_id: UUID = Field(foreign_key="policy.id", primary_key=True) + """存储策略UUID""" class PolicyType(StrEnum): LOCAL = "local" @@ -60,6 +72,12 @@ class Policy(UUIDTableBase, table=True): # 关系 objects: list["Object"] = Relationship(back_populates="policy") """策略下的所有对象""" + + # 多对多关系:策略可以被多个用户组使用 + groups: list["Group"] = Relationship( + back_populates="policies", + link_model=GroupPolicyLink, + ) @staticmethod async def create( diff --git a/models/response.py b/models/response.py index dfa3201..5782284 100644 --- a/models/response.py +++ b/models/response.py @@ -10,17 +10,8 @@ from sqlmodel import Field from .base import SQLModelBase # [TODO] 未来把这拆了,直接按需返回状态码 -class ResponseModel(SQLModelBase): +class ResponseBase(SQLModelBase): """通用响应模型""" - code: int = Field(default=0, ge=0, lt=60000) - """系统内部状态码,0表示成功,其他表示失败""" - - data: Any = None - """响应数据""" - - msg: str | None = None - """响应消息,可以是错误消息或信息提示""" - instance_id: uuid.UUID = Field(default_factory=uuid.uuid4) """实例ID,用于标识请求的唯一性""" diff --git a/routers/controllers/admin.py b/routers/api/v1/admin/__init__.py similarity index 87% rename from routers/controllers/admin.py rename to routers/api/v1/admin/__init__.py index b7be713..b53f240 100644 --- a/routers/controllers/admin.py +++ b/routers/api/v1/admin/__init__.py @@ -5,7 +5,7 @@ from middleware.auth import AdminRequired from middleware.dependencies import SessionDep from models import User from models.user import UserPublic -from models.response import ResponseModel +from models.response import ResponseBase # 管理员根目录 /api/admin admin_router = APIRouter( @@ -68,7 +68,7 @@ admin_vas_router = APIRouter( description='Get site summary information', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_summary() -> ResponseModel: +def router_admin_get_summary() -> ResponseBase: """ 获取站点概况信息,包括用户数、分享数、文件数等。 @@ -83,7 +83,7 @@ def router_admin_get_summary() -> ResponseModel: description='Get community news', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_news() -> ResponseModel: +def router_admin_get_news() -> ResponseBase: """ 获取社区新闻信息,包括最新的动态和公告。 @@ -98,7 +98,7 @@ def router_admin_get_news() -> ResponseModel: description='Update settings', dependencies=[Depends(AdminRequired)], ) -def router_admin_update_settings() -> ResponseModel: +def router_admin_update_settings() -> ResponseBase: """ 更新站点设置,包括站点名称、描述等。 @@ -113,7 +113,7 @@ def router_admin_update_settings() -> ResponseModel: description='Get settings', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_settings() -> ResponseModel: +def router_admin_get_settings() -> ResponseBase: """ 获取站点设置,包括站点名称、描述等。 @@ -128,7 +128,7 @@ def router_admin_get_settings() -> ResponseModel: description='Get user group list', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_groups() -> ResponseModel: +def router_admin_get_groups() -> ResponseBase: """ 获取用户组列表,包括每个用户组的名称和权限信息。 @@ -143,7 +143,7 @@ def router_admin_get_groups() -> ResponseModel: description='Get user group information by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_group(group_id: int) -> ResponseModel: +def router_admin_get_group(group_id: int) -> ResponseBase: """ 根据用户组ID获取用户组信息,包括名称、权限等。 @@ -165,7 +165,7 @@ def router_admin_get_group_members( group_id: int, page: int = 1, page_size: int = 20 -) -> ResponseModel: +) -> ResponseBase: """ 根据用户组ID获取用户组成员列表。 @@ -185,7 +185,7 @@ def router_admin_get_group_members( description='Create a new user group', dependencies=[Depends(AdminRequired)], ) -def router_admin_create_group() -> ResponseModel: +def router_admin_create_group() -> ResponseBase: """ 创建一个新的用户组,设置名称和权限等信息。 @@ -200,7 +200,7 @@ def router_admin_create_group() -> ResponseModel: description='Update user group information by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_update_group(group_id: int) -> ResponseModel: +def router_admin_update_group(group_id: int) -> ResponseBase: """ 根据用户组ID更新用户组信息,包括名称、权限等。 @@ -218,7 +218,7 @@ def router_admin_update_group(group_id: int) -> ResponseModel: description='Delete user group by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_delete_group(group_id: int) -> ResponseModel: +def router_admin_delete_group(group_id: int) -> ResponseBase: """ 根据用户组ID删除用户组。 @@ -236,7 +236,7 @@ def router_admin_delete_group(group_id: int) -> ResponseModel: description='Get user information by ID', dependencies=[Depends(AdminRequired)], ) -async def router_admin_get_user(session: SessionDep, user_id: int) -> ResponseModel: +async def router_admin_get_user(session: SessionDep, user_id: int) -> ResponseBase: """ 根据用户ID获取用户信息,包括用户名、邮箱、注册时间等。 @@ -248,7 +248,7 @@ async def router_admin_get_user(session: SessionDep, user_id: int) -> ResponseMo ResponseModel: 包含用户信息的响应模型。 """ user = await User.get_exist_one(session, user_id) - return ResponseModel(data=user.to_public().model_dump()) + return ResponseBase(data=user.to_public().model_dump()) @admin_user_router.get( path='/list', @@ -260,7 +260,7 @@ async def router_admin_get_users( session: SessionDep, page: int = 1, page_size: int = 20 -) -> ResponseModel: +) -> ResponseBase: """ 获取用户列表,支持分页。 @@ -280,7 +280,7 @@ async def router_admin_get_users( offset=offset, limit=page_size ) - return ResponseModel( + return ResponseBase( data=[user.to_public().model_dump() for user in users] ) @@ -293,7 +293,7 @@ async def router_admin_get_users( async def router_admin_create_user( session: SessionDep, user: User, -) -> ResponseModel: +) -> ResponseBase: """ 创建一个新的用户,设置用户名、密码等信息。 @@ -302,12 +302,12 @@ async def router_admin_create_user( """ existing_user = await User.get(session, User.username == user.username) if existing_user: - return ResponseModel( + return ResponseBase( code=400, msg="User with this username already exists." ) user = await user.save(session) - return ResponseModel(data=user.to_public().model_dump()) + return ResponseBase(data=user.to_public().model_dump()) @admin_user_router.patch( path='/{user_id}', @@ -315,7 +315,7 @@ async def router_admin_create_user( description='Update user information by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_update_user(user_id: int) -> ResponseModel: +def router_admin_update_user(user_id: int) -> ResponseBase: """ 根据用户ID更新用户信息,包括用户名、邮箱等。 @@ -333,7 +333,7 @@ def router_admin_update_user(user_id: int) -> ResponseModel: description='Delete user by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_delete_user(user_id: int) -> ResponseModel: +def router_admin_delete_user(user_id: int) -> ResponseBase: """ 根据用户ID删除用户。 @@ -360,7 +360,7 @@ def router_admin_calibrate_storage(): description='Get file list', dependencies=[Depends(AdminRequired)], ) -def router_admin_get_file_list() -> ResponseModel: +def router_admin_get_file_list() -> ResponseBase: """ 获取文件列表,包括文件名称、大小、上传时间等。 @@ -375,7 +375,7 @@ def router_admin_get_file_list() -> ResponseModel: description='Preview file by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_preview_file(file_id: int) -> ResponseModel: +def router_admin_preview_file(file_id: int) -> ResponseBase: """ 根据文件ID预览文件内容。 @@ -393,7 +393,7 @@ def router_admin_preview_file(file_id: int) -> ResponseModel: 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: +def router_admin_ban_file(file_id: int) -> ResponseBase: """ 根据文件ID封禁文件。 @@ -413,7 +413,7 @@ def router_admin_ban_file(file_id: int) -> ResponseModel: description='Delete file by ID', dependencies=[Depends(AdminRequired)], ) -def router_admin_delete_file(file_id: int) -> ResponseModel: +def router_admin_delete_file(file_id: int) -> ResponseBase: """ 根据文件ID删除文件。 @@ -431,7 +431,7 @@ def router_admin_delete_file(file_id: int) -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_admin_aira2_test() -> ResponseModel: +def router_admin_aira2_test() -> ResponseBase: pass @admin_policy_router.get( @@ -440,7 +440,7 @@ def router_admin_aira2_test() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_list() -> ResponseModel: +def router_policy_list() -> ResponseBase: pass @admin_policy_router.post( @@ -449,7 +449,7 @@ def router_policy_list() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_test_path() -> ResponseModel: +def router_policy_test_path() -> ResponseBase: pass @admin_policy_router.post( @@ -458,7 +458,7 @@ def router_policy_test_path() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_test_slave() -> ResponseModel: +def router_policy_test_slave() -> ResponseBase: pass @admin_policy_router.post( @@ -467,7 +467,7 @@ def router_policy_test_slave() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_add_policy() -> ResponseModel: +def router_policy_add_policy() -> ResponseBase: pass @admin_policy_router.post( @@ -476,7 +476,7 @@ def router_policy_add_policy() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_add_cors() -> ResponseModel: +def router_policy_add_cors() -> ResponseBase: pass @admin_policy_router.post( @@ -485,7 +485,7 @@ def router_policy_add_cors() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_add_scf() -> ResponseModel: +def router_policy_add_scf() -> ResponseBase: pass @admin_policy_router.get( @@ -494,7 +494,7 @@ def router_policy_add_scf() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_onddrive_oauth() -> ResponseModel: +def router_policy_onddrive_oauth() -> ResponseBase: pass @admin_policy_router.get( @@ -503,7 +503,7 @@ def router_policy_onddrive_oauth() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_get_policy() -> ResponseModel: +def router_policy_get_policy() -> ResponseBase: pass @admin_policy_router.delete( @@ -512,5 +512,5 @@ def router_policy_get_policy() -> ResponseModel: description='', dependencies=[Depends(AdminRequired)] ) -def router_policy_delete_policy() -> ResponseModel: +def router_policy_delete_policy() -> ResponseBase: pass \ No newline at end of file diff --git a/routers/controllers/callback.py b/routers/api/v1/callback/__init__.py similarity index 92% rename from routers/controllers/callback.py rename to routers/api/v1/callback/__init__.py index 32798fa..8355ce0 100644 --- a/routers/controllers/callback.py +++ b/routers/api/v1/callback/__init__.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, Query from fastapi.responses import PlainTextResponse, RedirectResponse from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase import service.oauth callback_router = APIRouter( @@ -33,7 +33,7 @@ callback_router.include_router(upload_router) summary='QQ互联回调', description='Handle QQ OAuth callback and return user information.', ) -def router_callback_qq() -> ResponseModel: +def router_callback_qq() -> ResponseBase: """ Handle QQ OAuth callback and return user information. @@ -79,7 +79,7 @@ async def router_callback_github( summary='支付宝支付回调', description='Handle Alipay payment callback and return payment status.', ) -def router_callback_alipay() -> ResponseModel: +def router_callback_alipay() -> ResponseBase: """ Handle Alipay payment callback and return payment status. @@ -93,7 +93,7 @@ def router_callback_alipay() -> ResponseModel: summary='微信支付回调', description='Handle WeChat Pay payment callback and return payment status.', ) -def router_callback_wechat() -> ResponseModel: +def router_callback_wechat() -> ResponseBase: """ Handle WeChat Pay payment callback and return payment status. @@ -107,7 +107,7 @@ def router_callback_wechat() -> ResponseModel: summary='Stripe支付回调', description='Handle Stripe payment callback and return payment status.', ) -def router_callback_stripe() -> ResponseModel: +def router_callback_stripe() -> ResponseBase: """ Handle Stripe payment callback and return payment status. @@ -136,7 +136,7 @@ def router_callback_easypay() -> PlainTextResponse: summary='自定义支付回调', description='Handle custom payment callback and return payment status.', ) -def router_callback_custom(order_no: str, id: str) -> ResponseModel: +def router_callback_custom(order_no: str, id: str) -> ResponseBase: """ Handle custom payment callback and return payment status. @@ -154,7 +154,7 @@ def router_callback_custom(order_no: str, id: str) -> ResponseModel: summary='远程上传回调', description='Handle remote upload callback and return upload status.', ) -def router_callback_remote(session_id: str, key: str) -> ResponseModel: +def router_callback_remote(session_id: str, key: str) -> ResponseBase: """ Handle remote upload callback and return upload status. @@ -172,7 +172,7 @@ def router_callback_remote(session_id: str, key: str) -> ResponseModel: summary='七牛云上传回调', description='Handle Qiniu Cloud upload callback and return upload status.', ) -def router_callback_qiniu(session_id: str) -> ResponseModel: +def router_callback_qiniu(session_id: str) -> ResponseBase: """ Handle Qiniu Cloud upload callback and return upload status. @@ -189,7 +189,7 @@ def router_callback_qiniu(session_id: str) -> ResponseModel: summary='腾讯云上传回调', description='Handle Tencent Cloud upload callback and return upload status.', ) -def router_callback_tencent(session_id: str) -> ResponseModel: +def router_callback_tencent(session_id: str) -> ResponseBase: """ Handle Tencent Cloud upload callback and return upload status. @@ -206,7 +206,7 @@ def router_callback_tencent(session_id: str) -> ResponseModel: summary='阿里云上传回调', description='Handle Aliyun upload callback and return upload status.', ) -def router_callback_aliyun(session_id: str) -> ResponseModel: +def router_callback_aliyun(session_id: str) -> ResponseBase: """ Handle Aliyun upload callback and return upload status. @@ -223,7 +223,7 @@ def router_callback_aliyun(session_id: str) -> ResponseModel: summary='又拍云上传回调', description='Handle Upyun upload callback and return upload status.', ) -def router_callback_upyun(session_id: str) -> ResponseModel: +def router_callback_upyun(session_id: str) -> ResponseBase: """ Handle Upyun upload callback and return upload status. @@ -240,7 +240,7 @@ def router_callback_upyun(session_id: str) -> ResponseModel: summary='AWS S3上传回调', description='Handle AWS S3 upload callback and return upload status.', ) -def router_callback_aws(session_id: str) -> ResponseModel: +def router_callback_aws(session_id: str) -> ResponseBase: """ Handle AWS S3 upload callback and return upload status. @@ -257,7 +257,7 @@ def router_callback_aws(session_id: str) -> ResponseModel: summary='OneDrive上传完成回调', description='Handle OneDrive upload completion callback and return upload status.', ) -def router_callback_onedrive_finish(session_id: str) -> ResponseModel: +def router_callback_onedrive_finish(session_id: str) -> ResponseBase: """ Handle OneDrive upload completion callback and return upload status. @@ -274,7 +274,7 @@ def router_callback_onedrive_finish(session_id: str) -> ResponseModel: summary='OneDrive授权回调', description='Handle OneDrive authorization callback and return authorization status.', ) -def router_callback_onedrive_auth() -> ResponseModel: +def router_callback_onedrive_auth() -> ResponseBase: """ Handle OneDrive authorization callback and return authorization status. @@ -288,7 +288,7 @@ def router_callback_onedrive_auth() -> ResponseModel: summary='Google OAuth 完成', description='Handle Google OAuth completion callback and return authorization status.', ) -def router_callback_google_auth() -> ResponseModel: +def router_callback_google_auth() -> ResponseBase: """ Handle Google OAuth completion callback and return authorization status. diff --git a/routers/controllers/directory.py b/routers/api/v1/directory/__init__.py similarity index 98% rename from routers/controllers/directory.py rename to routers/api/v1/directory/__init__.py index 4fbb3e6..8da3894 100644 --- a/routers/controllers/directory.py +++ b/routers/api/v1/directory/__init__.py @@ -97,7 +97,7 @@ async def router_directory_create( session: SessionDep, user: Annotated[User, Depends(AuthRequired)], request: DirectoryCreateRequest -) -> response.ResponseModel: +) -> response.ResponseBase: """ 创建目录 @@ -146,7 +146,7 @@ async def router_directory_create( new_folder_name = new_folder.name await new_folder.save(session) - return response.ResponseModel( + return response.ResponseBase( data={ "id": new_folder_id, "name": new_folder_name, diff --git a/routers/controllers/aria2.py b/routers/api/v1/download/__init__.py similarity index 88% rename from routers/controllers/aria2.py rename to routers/api/v1/download/__init__.py index 91ed33d..d566e83 100644 --- a/routers/controllers/aria2.py +++ b/routers/api/v1/download/__init__.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase aria2_router = APIRouter( prefix="/aria2", @@ -13,7 +13,7 @@ aria2_router = APIRouter( description='Create a URL download task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_url() -> ResponseModel: +def router_aria2_url() -> ResponseBase: """ Create a URL download task endpoint. @@ -28,7 +28,7 @@ def router_aria2_url() -> ResponseModel: description='Create a torrent download task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_torrent(id: str) -> ResponseModel: +def router_aria2_torrent(id: str) -> ResponseBase: """ Create a torrent download task endpoint. @@ -46,7 +46,7 @@ def router_aria2_torrent(id: str) -> ResponseModel: description='Re-select files to download endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_select(gid: str) -> ResponseModel: +def router_aria2_select(gid: str) -> ResponseBase: """ Re-select files to download endpoint. @@ -64,7 +64,7 @@ def router_aria2_select(gid: str) -> ResponseModel: description='Delete a download task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_delete(gid: str) -> ResponseModel: +def router_aria2_delete(gid: str) -> ResponseBase: """ Delete a download task endpoint. @@ -82,7 +82,7 @@ def router_aria2_delete(gid: str) -> ResponseModel: description='Get currently downloading tasks endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_downloading() -> ResponseModel: +def router_aria2_downloading() -> ResponseBase: """ Get currently downloading tasks endpoint. @@ -97,7 +97,7 @@ def router_aria2_downloading() -> ResponseModel: description='Get finished tasks endpoint.', dependencies=[Depends(SignRequired)] ) -def router_aria2_finished() -> ResponseModel: +def router_aria2_finished() -> ResponseBase: """ Get finished tasks endpoint. diff --git a/routers/controllers/file.py b/routers/api/v1/file/__init__.py similarity index 89% rename from routers/controllers/file.py rename to routers/api/v1/file/__init__.py index f36219c..7703990 100644 --- a/routers/controllers/file.py +++ b/routers/api/v1/file/__init__.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, UploadFile from fastapi.responses import FileResponse from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase file_router = APIRouter( prefix="/file", @@ -36,7 +36,7 @@ def router_file_get(id: str, name: str) -> FileResponse: summary='文件外链(301跳转)', description='Get file external link with 301 redirect endpoint.', ) -def router_file_source(id: str, name: str) -> ResponseModel: +def router_file_source(id: str, name: str) -> ResponseBase: """ Get file external link with 301 redirect endpoint. @@ -54,7 +54,7 @@ def router_file_source(id: str, name: str) -> ResponseModel: summary='下载文件', description='Download file endpoint.', ) -def router_file_download(id: str) -> ResponseModel: +def router_file_download(id: str) -> ResponseBase: """ Download file endpoint. @@ -71,7 +71,7 @@ def router_file_download(id: str) -> ResponseModel: summary='打包并下载文件', description='Archive and download files endpoint.', ) -def router_file_archive_download(sessionID: str) -> ResponseModel: +def router_file_archive_download(sessionID: str) -> ResponseBase: """ Archive and download files endpoint. @@ -88,7 +88,7 @@ def router_file_archive_download(sessionID: str) -> ResponseModel: summary='文件上传', description='File upload endpoint.', ) -def router_file_upload(sessionID: str, index: int, file: UploadFile) -> ResponseModel: +def router_file_upload(sessionID: str, index: int, file: UploadFile) -> ResponseBase: """ File upload endpoint. @@ -107,7 +107,7 @@ def router_file_upload(sessionID: str, index: int, file: UploadFile) -> Response description='Create an upload session endpoint.', dependencies=[Depends(SignRequired)], ) -def router_file_upload_session() -> ResponseModel: +def router_file_upload_session() -> ResponseBase: """ Create an upload session endpoint. @@ -122,7 +122,7 @@ def router_file_upload_session() -> ResponseModel: description='Delete an upload session endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_upload_session_delete(sessionID: str) -> ResponseModel: +def router_file_upload_session_delete(sessionID: str) -> ResponseBase: """ Delete an upload session endpoint. @@ -140,7 +140,7 @@ def router_file_upload_session_delete(sessionID: str) -> ResponseModel: description='Clear all upload sessions endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_upload_session_clear() -> ResponseModel: +def router_file_upload_session_clear() -> ResponseBase: """ Clear all upload sessions endpoint. @@ -155,7 +155,7 @@ def router_file_upload_session_clear() -> ResponseModel: description='Update file information endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_update(id: str) -> ResponseModel: +def router_file_update(id: str) -> ResponseBase: """ Update file information endpoint. @@ -173,7 +173,7 @@ def router_file_update(id: str) -> ResponseModel: description='Create a blank file endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_create() -> ResponseModel: +def router_file_create() -> ResponseBase: """ Create a blank file endpoint. @@ -188,7 +188,7 @@ def router_file_create() -> ResponseModel: description='Create a file download session endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_download(id: str) -> ResponseModel: +def router_file_download(id: str) -> ResponseBase: """ Create a file download session endpoint. @@ -206,7 +206,7 @@ def router_file_download(id: str) -> ResponseModel: description='Preview file endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_preview(id: str) -> ResponseModel: +def router_file_preview(id: str) -> ResponseBase: """ Preview file endpoint. @@ -224,7 +224,7 @@ def router_file_preview(id: str) -> ResponseModel: description='Get text file content endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_content(id: str) -> ResponseModel: +def router_file_content(id: str) -> ResponseBase: """ Get text file content endpoint. @@ -242,7 +242,7 @@ def router_file_content(id: str) -> ResponseModel: description='Get Office document preview URL endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_doc(id: str) -> ResponseModel: +def router_file_doc(id: str) -> ResponseBase: """ Get Office document preview URL endpoint. @@ -260,7 +260,7 @@ def router_file_doc(id: str) -> ResponseModel: description='Get file thumbnail endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_thumb(id: str) -> ResponseModel: +def router_file_thumb(id: str) -> ResponseBase: """ Get file thumbnail endpoint. @@ -278,7 +278,7 @@ def router_file_thumb(id: str) -> ResponseModel: description='Get file external link endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_source(id: str) -> ResponseModel: +def router_file_source(id: str) -> ResponseBase: """ Get file external link endpoint. @@ -296,7 +296,7 @@ def router_file_source(id: str) -> ResponseModel: description='Archive files for download endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_archive(id: str) -> ResponseModel: +def router_file_archive(id: str) -> ResponseBase: """ Archive files for download endpoint. @@ -314,7 +314,7 @@ def router_file_archive(id: str) -> ResponseModel: description='Create file compression task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_compress(id: str) -> ResponseModel: +def router_file_compress(id: str) -> ResponseBase: """ Create file compression task endpoint. @@ -332,7 +332,7 @@ def router_file_compress(id: str) -> ResponseModel: description='Create file extraction task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_decompress(id: str) -> ResponseModel: +def router_file_decompress(id: str) -> ResponseBase: """ Create file extraction task endpoint. @@ -350,7 +350,7 @@ def router_file_decompress(id: str) -> ResponseModel: description='Create file relocation task endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_relocate(id: str) -> ResponseModel: +def router_file_relocate(id: str) -> ResponseBase: """ Create file relocation task endpoint. @@ -368,7 +368,7 @@ def router_file_relocate(id: str) -> ResponseModel: description='Search files by keyword endpoint.', dependencies=[Depends(SignRequired)] ) -def router_file_search(type: str, keyword: str) -> ResponseModel: +def router_file_search(type: str, keyword: str) -> ResponseBase: """ Search files by keyword endpoint. diff --git a/routers/controllers/object.py b/routers/api/v1/object/__init__.py similarity index 93% rename from routers/controllers/object.py rename to routers/api/v1/object/__init__.py index 39e4b0e..f60f4ba 100644 --- a/routers/controllers/object.py +++ b/routers/api/v1/object/__init__.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException from middleware.auth import AuthRequired from middleware.dependencies import SessionDep from models import Object, ObjectDeleteRequest, ObjectMoveRequest, User -from models.response import ResponseModel +from models.response import ResponseBase object_router = APIRouter( prefix="/object", @@ -22,7 +22,7 @@ async def router_object_delete( session: SessionDep, user: Annotated[User, Depends(AuthRequired)], request: ObjectDeleteRequest, -) -> ResponseModel: +) -> ResponseBase: """ 删除对象端点 @@ -41,7 +41,7 @@ async def router_object_delete( await obj.delete(session) deleted_count += 1 - return ResponseModel( + return ResponseBase( data={ "deleted": deleted_count, "total": len(request.ids), @@ -58,7 +58,7 @@ async def router_object_move( session: SessionDep, user: Annotated[User, Depends(AuthRequired)], request: ObjectMoveRequest, -) -> ResponseModel: +) -> ResponseBase: """ 移动对象端点 @@ -100,7 +100,7 @@ async def router_object_move( await src.save(session) moved_count += 1 - return ResponseModel( + return ResponseBase( data={ "moved": moved_count, "total": len(request.src_ids), @@ -113,7 +113,7 @@ async def router_object_move( description='Copy an object endpoint.', dependencies=[Depends(AuthRequired)] ) -def router_object_copy() -> ResponseModel: +def router_object_copy() -> ResponseBase: """ Copy an object endpoint. @@ -128,7 +128,7 @@ def router_object_copy() -> ResponseModel: description='Rename an object endpoint.', dependencies=[Depends(AuthRequired)] ) -def router_object_rename() -> ResponseModel: +def router_object_rename() -> ResponseBase: """ Rename an object endpoint. @@ -143,7 +143,7 @@ def router_object_rename() -> ResponseModel: description='Get object properties endpoint.', dependencies=[Depends(AuthRequired)] ) -def router_object_property(id: str) -> ResponseModel: +def router_object_property(id: str) -> ResponseBase: """ Get object properties endpoint. diff --git a/routers/controllers/site.py b/routers/api/v1/site/__init__.py similarity index 92% rename from routers/controllers/site.py rename to routers/api/v1/site/__init__.py index ffa2bc7..6254d97 100644 --- a/routers/controllers/site.py +++ b/routers/api/v1/site/__init__.py @@ -3,7 +3,7 @@ from sqlalchemy import and_ import json from middleware.dependencies import SessionDep -from models.response import ResponseModel +from models.response import ResponseBase from models.setting import Setting site_router = APIRouter( @@ -33,7 +33,7 @@ async def _get_setting_json(session: SessionDep, type_: str, name: str) -> dict path="/ping", summary="测试用路由", description="A simple endpoint to check if the site is up and running.", - response_model=ResponseModel, + response_model=ResponseBase, ) def router_site_ping(): """ @@ -42,15 +42,15 @@ def router_site_ping(): Returns: str: A message indicating the site is running. """ - from pkg.conf.appmeta import BackendVersion - return ResponseModel(data=BackendVersion) + from utils.conf.appmeta import BackendVersion + return ResponseBase(data=BackendVersion) @site_router.get( path='/captcha', summary='验证码', description='Get a Base64 captcha image.', - response_model=ResponseModel, + response_model=ResponseBase, ) def router_site_captcha(): """ @@ -66,7 +66,7 @@ def router_site_captcha(): path='/config', summary='站点全局配置', description='Get the configuration file.', - response_model=ResponseModel, + response_model=ResponseBase, ) async def router_site_config(session: SessionDep): """ @@ -75,7 +75,7 @@ async def router_site_config(session: SessionDep): Returns: dict: The site configuration. """ - return ResponseModel( + return ResponseBase( data={ "title": await _get_setting(session, "basic", "siteName"), "loginCaptcha": await _get_setting_bool(session, "login", "login_captcha"), diff --git a/routers/controllers/slave.py b/routers/api/v1/slave/__init__.py similarity index 90% rename from routers/controllers/slave.py rename to routers/api/v1/slave/__init__.py index a278cb7..e5babd9 100644 --- a/routers/controllers/slave.py +++ b/routers/api/v1/slave/__init__.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends from fastapi.responses import FileResponse from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase slave_router = APIRouter( prefix="/slave", @@ -18,15 +18,15 @@ slave_aria2_router = APIRouter( summary='测试用路由', description='Test route for checking connectivity.', ) -def router_slave_ping() -> ResponseModel: +def router_slave_ping() -> ResponseBase: """ Test route for checking connectivity. Returns: ResponseModel: A response model indicating success. """ - from pkg.conf.appmeta import BackendVersion - return ResponseModel(data=BackendVersion) + from utils.conf.appmeta import BackendVersion + return ResponseBase(data=BackendVersion) @slave_router.post( path='/post', @@ -34,7 +34,7 @@ def router_slave_ping() -> ResponseModel: description='Upload data to the server.', dependencies=[Depends(SignRequired)], ) -def router_slave_post(data: str) -> ResponseModel: +def router_slave_post(data: str) -> ResponseBase: """ Upload data to the server. @@ -50,7 +50,7 @@ def router_slave_post(data: str) -> ResponseModel: path='/get/{speed}/{path}/{name}', summary='获取下载', ) -def router_slave_download(speed: int, path: str, name: str) -> ResponseModel: +def router_slave_download(speed: int, path: str, name: str) -> ResponseBase: """ Get download information. @@ -88,7 +88,7 @@ def router_slave_download_by_sign(sign: str) -> FileResponse: description='Get the external link for a file based on its signature.', dependencies=[Depends(SignRequired)], ) -def router_slave_source(speed: int, path: str, name: str) -> ResponseModel: +def router_slave_source(speed: int, path: str, name: str) -> ResponseBase: """ Get the external link for a file based on its signature. @@ -126,7 +126,7 @@ def router_slave_source_by_sign(sign: str) -> FileResponse: description='Get a thumbnail image based on its ID.', dependencies=[Depends(SignRequired)], ) -def router_slave_thumb(id: str) -> ResponseModel: +def router_slave_thumb(id: str) -> ResponseBase: """ Get a thumbnail image based on its ID. @@ -144,7 +144,7 @@ def router_slave_thumb(id: str) -> ResponseModel: description='Delete a file from the server.', dependencies=[Depends(SignRequired)], ) -def router_slave_delete(path: str) -> ResponseModel: +def router_slave_delete(path: str) -> ResponseBase: """ Delete a file from the server. @@ -162,7 +162,7 @@ def router_slave_delete(path: str) -> ResponseModel: description='Test the connection to the Aria2 service from the slave.', dependencies=[Depends(SignRequired)], ) -def router_slave_aria2_test() -> ResponseModel: +def router_slave_aria2_test() -> ResponseBase: """ Test the connection to the Aria2 service from the slave. """ @@ -174,7 +174,7 @@ def router_slave_aria2_test() -> ResponseModel: description='Get information about an Aria2 task by its GID.', dependencies=[Depends(SignRequired)], ) -def router_slave_aria2_get(gid: str = None) -> ResponseModel: +def router_slave_aria2_get(gid: str = None) -> ResponseBase: """ Get information about an Aria2 task by its GID. @@ -192,7 +192,7 @@ def router_slave_aria2_get(gid: str = None) -> ResponseModel: description='Add a new Aria2 task.', dependencies=[Depends(SignRequired)], ) -def router_slave_aria2_add(gid: str, url: str, options: dict = None) -> ResponseModel: +def router_slave_aria2_add(gid: str, url: str, options: dict = None) -> ResponseBase: """ Add a new Aria2 task. @@ -212,7 +212,7 @@ def router_slave_aria2_add(gid: str, url: str, options: dict = None) -> Response description='Remove an Aria2 task by its GID.', dependencies=[Depends(SignRequired)], ) -def router_slave_aria2_remove(gid: str) -> ResponseModel: +def router_slave_aria2_remove(gid: str) -> ResponseBase: """ Remove an Aria2 task by its GID. diff --git a/routers/controllers/tag.py b/routers/api/v1/tag/__init__.py similarity index 86% rename from routers/controllers/tag.py rename to routers/api/v1/tag/__init__.py index 200dd73..7ce3685 100644 --- a/routers/controllers/tag.py +++ b/routers/api/v1/tag/__init__.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase tag_router = APIRouter( prefix='/tag', @@ -13,7 +13,7 @@ tag_router = APIRouter( description='Create a file classification tag.', dependencies=[Depends(SignRequired)], ) -def router_tag_create_filter() -> ResponseModel: +def router_tag_create_filter() -> ResponseBase: """ Create a file classification tag. @@ -28,7 +28,7 @@ def router_tag_create_filter() -> ResponseModel: description='Create a directory shortcut tag.', dependencies=[Depends(SignRequired)], ) -def router_tag_create_link() -> ResponseModel: +def router_tag_create_link() -> ResponseBase: """ Create a directory shortcut tag. @@ -43,7 +43,7 @@ def router_tag_create_link() -> ResponseModel: description='Delete a tag by its ID.', dependencies=[Depends(SignRequired)], ) -def router_tag_delete(id: str) -> ResponseModel: +def router_tag_delete(id: str) -> ResponseBase: """ Delete a tag by its ID. diff --git a/routers/controllers/user.py b/routers/api/v1/user/__init__.py similarity index 91% rename from routers/controllers/user.py rename to routers/api/v1/user/__init__.py index c426119..69a966f 100644 --- a/routers/controllers/user.py +++ b/routers/api/v1/user/__init__.py @@ -12,8 +12,8 @@ import models import service from middleware.auth import AuthRequired from middleware.dependencies import SessionDep -from pkg.JWT.JWT import SECRET_KEY -from pkg import Password +from utils.JWT.JWT import SECRET_KEY +from utils import Password user_router = APIRouter( prefix="/user", @@ -96,7 +96,7 @@ async def router_user_session( async def router_user_register( session: SessionDep, request: models.RegisterRequest, -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ 用户注册端点 @@ -157,7 +157,7 @@ async def router_user_register( policy_id=default_policy.id, ).save(session) - return models.response.ResponseModel( + return models.response.ResponseBase( data={ "user_id": new_user_id, "username": new_user_username, @@ -172,7 +172,7 @@ async def router_user_register( ) def router_user_email_code( reason: Literal['register', 'reset'] = 'register', -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ Send a verification code email. @@ -186,7 +186,7 @@ def router_user_email_code( summary='初始化QQ登录', description='Initialize QQ login for a user.', ) -def router_user_qq() -> models.response.ResponseModel: +def router_user_qq() -> models.response.ResponseBase: """ Initialize QQ login for a user. @@ -200,7 +200,7 @@ def router_user_qq() -> models.response.ResponseModel: summary='WebAuthn登录初始化', description='Initialize WebAuthn login for a user.', ) -async def router_user_authn(username: str) -> models.response.ResponseModel: +async def router_user_authn(username: str) -> models.response.ResponseBase: pass @@ -209,7 +209,7 @@ async def router_user_authn(username: str) -> models.response.ResponseModel: summary='WebAuthn登录', description='Finish WebAuthn login for a user.', ) -def router_user_authn_finish(username: str) -> models.response.ResponseModel: +def router_user_authn_finish(username: str) -> models.response.ResponseBase: """ Finish WebAuthn login for a user. @@ -226,7 +226,7 @@ def router_user_authn_finish(username: str) -> models.response.ResponseModel: summary='获取用户主页展示用分享', description='Get user profile for display.', ) -def router_user_profile(id: str) -> models.response.ResponseModel: +def router_user_profile(id: str) -> models.response.ResponseBase: """ Get user profile for display. @@ -243,7 +243,7 @@ def router_user_profile(id: str) -> models.response.ResponseModel: summary='获取用户头像', description='Get user avatar by ID and size.', ) -def router_user_avatar(id: str, size: int = 128) -> models.response.ResponseModel: +def router_user_avatar(id: str, size: int = 128) -> models.response.ResponseBase: """ Get user avatar by ID and size. @@ -265,12 +265,12 @@ def router_user_avatar(id: str, size: int = 128) -> models.response.ResponseMode summary='获取用户信息', description='Get user information.', dependencies=[Depends(dependency=AuthRequired)], - response_model=models.response.ResponseModel, + response_model=models.response.ResponseBase, ) async def router_user_me( session: SessionDep, user: Annotated[models.User, Depends(AuthRequired)], -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ 获取用户信息. @@ -302,7 +302,7 @@ async def router_user_me( tags=[tag.name for tag in user_tags] if user_tags else [], ) - return models.response.ResponseModel(data=user_response.model_dump()) + return models.response.ResponseBase(data=user_response.model_dump()) @user_router.get( path='/storage', @@ -313,7 +313,7 @@ async def router_user_me( async def router_user_storage( session: SessionDep, user: Annotated[models.user.User, Depends(AuthRequired)], -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ 获取用户存储空间信息。 @@ -330,7 +330,7 @@ async def router_user_storage( used: int = user.storage free: int = max(0, total - used) - return models.response.ResponseModel( + return models.response.ResponseBase( data={ "used": used, "free": free, @@ -347,7 +347,7 @@ async def router_user_storage( async def router_user_authn_start( session: SessionDep, user: Annotated[models.user.User, Depends(AuthRequired)], -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ Initialize WebAuthn login for a user. @@ -378,7 +378,7 @@ async def router_user_authn_start( user_display_name=user.nick or user.username, ) - return models.response.ResponseModel(data=options_to_json_dict(options)) + return models.response.ResponseBase(data=options_to_json_dict(options)) @user_router.put( path='/authn/finish', @@ -386,7 +386,7 @@ async def router_user_authn_start( description='Finish WebAuthn login for a user.', dependencies=[Depends(AuthRequired)], ) -def router_user_authn_finish() -> models.response.ResponseModel: +def router_user_authn_finish() -> models.response.ResponseBase: """ Finish WebAuthn login for a user. @@ -400,7 +400,7 @@ def router_user_authn_finish() -> models.response.ResponseModel: summary='获取用户可选存储策略', description='Get user selectable storage policies.', ) -def router_user_settings_policies() -> models.response.ResponseModel: +def router_user_settings_policies() -> models.response.ResponseBase: """ Get user selectable storage policies. @@ -415,7 +415,7 @@ def router_user_settings_policies() -> models.response.ResponseModel: description='Get user selectable nodes.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings_nodes() -> models.response.ResponseModel: +def router_user_settings_nodes() -> models.response.ResponseBase: """ Get user selectable nodes. @@ -430,7 +430,7 @@ def router_user_settings_nodes() -> models.response.ResponseModel: description='Get user task queue.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings_tasks() -> models.response.ResponseModel: +def router_user_settings_tasks() -> models.response.ResponseBase: """ Get user task queue. @@ -445,14 +445,14 @@ def router_user_settings_tasks() -> models.response.ResponseModel: description='Get current user settings.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings() -> models.response.ResponseModel: +def router_user_settings() -> models.response.ResponseBase: """ Get current user settings. Returns: dict: A dictionary containing the current user settings. """ - return models.response.ResponseModel(data=models.UserSettingResponse().model_dump()) + return models.response.ResponseBase(data=models.UserSettingResponse().model_dump()) @user_settings_router.post( path='/avatar', @@ -460,7 +460,7 @@ def router_user_settings() -> models.response.ResponseModel: description='Upload user avatar from file.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings_avatar() -> models.response.ResponseModel: +def router_user_settings_avatar() -> models.response.ResponseBase: """ Upload user avatar from file. @@ -475,7 +475,7 @@ def router_user_settings_avatar() -> models.response.ResponseModel: description='Set user avatar to Gravatar.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings_avatar_gravatar() -> models.response.ResponseModel: +def router_user_settings_avatar_gravatar() -> models.response.ResponseBase: """ Set user avatar to Gravatar. @@ -490,7 +490,7 @@ def router_user_settings_avatar_gravatar() -> models.response.ResponseModel: description='Update user settings.', dependencies=[Depends(AuthRequired)], ) -def router_user_settings_patch(option: str) -> models.response.ResponseModel: +def router_user_settings_patch(option: str) -> models.response.ResponseBase: """ Update user settings. @@ -510,7 +510,7 @@ def router_user_settings_patch(option: str) -> models.response.ResponseModel: ) async def router_user_settings_2fa( user: Annotated[models.user.User, Depends(AuthRequired)], -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ Get two-factor authentication initialization information. @@ -518,7 +518,7 @@ async def router_user_settings_2fa( dict: A dictionary containing two-factor authentication setup information. """ - return models.response.ResponseModel( + return models.response.ResponseBase( data=await Password.generate_totp(user.username) ) @@ -533,7 +533,7 @@ async def router_user_settings_2fa_enable( user: Annotated[models.user.User, Depends(AuthRequired)], setup_token: str, code: str, -) -> models.response.ResponseModel: +) -> models.response.ResponseBase: """ Enable two-factor authentication for the user. @@ -559,6 +559,6 @@ async def router_user_settings_2fa_enable( user.two_factor = secret user = await user.save(session) - return models.response.ResponseModel( + return models.response.ResponseBase( data={"message": "Two-factor authentication enabled successfully"} ) \ No newline at end of file diff --git a/routers/controllers/vas.py b/routers/api/v1/vas/__init__.py similarity index 88% rename from routers/controllers/vas.py rename to routers/api/v1/vas/__init__.py index 0308c74..5ed7598 100644 --- a/routers/controllers/vas.py +++ b/routers/api/v1/vas/__init__.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase vas_router = APIRouter( prefix="/vas", @@ -13,7 +13,7 @@ vas_router = APIRouter( description='Get information about storage packs and quotas.', dependencies=[Depends(SignRequired)] ) -def router_vas_pack() -> ResponseModel: +def router_vas_pack() -> ResponseBase: """ Get information about storage packs and quotas. @@ -28,7 +28,7 @@ def router_vas_pack() -> ResponseModel: description='Get product information along with payment details.', dependencies=[Depends(SignRequired)] ) -def router_vas_product() -> ResponseModel: +def router_vas_product() -> ResponseBase: """ Get product information along with payment details. @@ -43,7 +43,7 @@ def router_vas_product() -> ResponseModel: description='Create an order for a product.', dependencies=[Depends(SignRequired)] ) -def router_vas_order() -> ResponseModel: +def router_vas_order() -> ResponseBase: """ Create an order for a product. @@ -58,7 +58,7 @@ def router_vas_order() -> ResponseModel: description='Get information about a specific payment order by ID.', dependencies=[Depends(SignRequired)] ) -def router_vas_order_get(id: str) -> ResponseModel: +def router_vas_order_get(id: str) -> ResponseBase: """ Get information about a specific payment order by ID. @@ -76,7 +76,7 @@ def router_vas_order_get(id: str) -> ResponseModel: description='Get information about a specific redemption code.', dependencies=[Depends(SignRequired)] ) -def router_vas_redeem(code: str) -> ResponseModel: +def router_vas_redeem(code: str) -> ResponseBase: """ Get information about a specific redemption code. @@ -94,7 +94,7 @@ def router_vas_redeem(code: str) -> ResponseModel: description='Redeem a redemption code for a product or service.', dependencies=[Depends(SignRequired)] ) -def router_vas_redeem_post() -> ResponseModel: +def router_vas_redeem_post() -> ResponseBase: """ Redeem a redemption code for a product or service. diff --git a/routers/controllers/webdav.py b/routers/api/v1/webdav/__init__.py similarity index 86% rename from routers/controllers/webdav.py rename to routers/api/v1/webdav/__init__.py index 5656c99..718b969 100644 --- a/routers/controllers/webdav.py +++ b/routers/api/v1/webdav/__init__.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends, Request from middleware.auth import SignRequired -from models.response import ResponseModel +from models.response import ResponseBase # WebDAV 管理路由 webdav_router = APIRouter( @@ -14,7 +14,7 @@ webdav_router = APIRouter( description='Get account information for WebDAV.', dependencies=[Depends(SignRequired)], ) -def router_webdav_accounts() -> ResponseModel: +def router_webdav_accounts() -> ResponseBase: """ Get account information for WebDAV. @@ -29,7 +29,7 @@ def router_webdav_accounts() -> ResponseModel: description='Create a new WebDAV account.', dependencies=[Depends(SignRequired)], ) -def router_webdav_create_account() -> ResponseModel: +def router_webdav_create_account() -> ResponseBase: """ Create a new WebDAV account. @@ -44,7 +44,7 @@ def router_webdav_create_account() -> ResponseModel: description='Delete a WebDAV account by its ID.', dependencies=[Depends(SignRequired)], ) -def router_webdav_delete_account(id: str) -> ResponseModel: +def router_webdav_delete_account(id: str) -> ResponseBase: """ Delete a WebDAV account by its ID. @@ -62,7 +62,7 @@ def router_webdav_delete_account(id: str) -> ResponseModel: description='Create a new WebDAV mount point.', dependencies=[Depends(SignRequired)], ) -def router_webdav_create_mount() -> ResponseModel: +def router_webdav_create_mount() -> ResponseBase: """ Create a new WebDAV mount point. @@ -77,7 +77,7 @@ def router_webdav_create_mount() -> ResponseModel: description='Delete a WebDAV mount point by its ID.', dependencies=[Depends(SignRequired)], ) -def router_webdav_delete_mount(id: str) -> ResponseModel: +def router_webdav_delete_mount(id: str) -> ResponseBase: """ Delete a WebDAV mount point by its ID. @@ -95,7 +95,7 @@ def router_webdav_delete_mount(id: str) -> ResponseModel: description='Update WebDAV account information by ID.', dependencies=[Depends(SignRequired)], ) -def router_webdav_update_account(id: str) -> ResponseModel: +def router_webdav_update_account(id: str) -> ResponseBase: """ Update WebDAV account information by ID. diff --git a/routers/controllers/share.py b/routers/controllers/share.py deleted file mode 100644 index 5a20094..0000000 --- a/routers/controllers/share.py +++ /dev/null @@ -1,306 +0,0 @@ -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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -##################### -# 需要登录的接口 -##################### - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass - -@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. - """ - pass \ No newline at end of file diff --git a/routers/dav/README.md b/routers/dav/README.md new file mode 100644 index 0000000..33142d9 --- /dev/null +++ b/routers/dav/README.md @@ -0,0 +1 @@ +# WebDAV 操作路由 \ No newline at end of file diff --git a/routers/routers.py b/routers/routers.py deleted file mode 100644 index 23d54f5..0000000 --- a/routers/routers.py +++ /dev/null @@ -1,46 +0,0 @@ -from fastapi import APIRouter - -from .controllers import ( - share, - site, - user, - file, - aria2, - directory, - object, - callback, - vas, - tag, - webdav, - admin, - slave -) - -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, - slave.slave_router, - slave.slave_aria2_router -] \ No newline at end of file diff --git a/service/user/login.py b/service/user/login.py index 422475f..9386eee 100644 --- a/service/user/login.py +++ b/service/user/login.py @@ -4,7 +4,7 @@ from loguru import logger as log from sqlmodel.ext.asyncio.session import AsyncSession from models import LoginRequest, TokenResponse, User -from pkg.JWT.JWT import create_access_token, create_refresh_token +from utils.JWT.JWT import create_access_token, create_refresh_token async def Login( @@ -25,7 +25,7 @@ async def Login( :return: TokenResponse 对象或状态码或 None """ - from pkg.password.pwd import Password + from utils.password.pwd import Password # TODO: 验证码校验 # captcha_setting = await Setting.get( diff --git a/tests/test_main.py b/tests/test_main.py index d5025d3..4f2658a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -15,7 +15,7 @@ def is_valid_instance_id(instance_id): assert False, f"instance_id is not a valid UUID4: {instance_id}" def test_read_main(): - from pkg.conf.appmeta import BackendVersion + from utils.conf.appmeta import BackendVersion response = client.get("/api/site/ping") json_response = response.json() diff --git a/tests/test_pkg_password.py b/tests/test_pkg_password.py index 2877230..76c17b0 100644 --- a/tests/test_pkg_password.py +++ b/tests/test_pkg_password.py @@ -1,5 +1,5 @@ import pytest -from pkg.password.pwd import Password +from utils.password.pwd import Password def test_password(): for i in range(10): diff --git a/pkg/JWT/jwt.py b/utils/JWT/JWT.py similarity index 100% rename from pkg/JWT/jwt.py rename to utils/JWT/JWT.py diff --git a/pkg/__init__.py b/utils/__init__.py similarity index 100% rename from pkg/__init__.py rename to utils/__init__.py diff --git a/pkg/conf/appmeta.py b/utils/conf/appmeta.py similarity index 100% rename from pkg/conf/appmeta.py rename to utils/conf/appmeta.py diff --git a/pkg/lifespan/lifespan.py b/utils/lifespan/lifespan.py similarity index 100% rename from pkg/lifespan/lifespan.py rename to utils/lifespan/lifespan.py diff --git a/pkg/password/pwd.py b/utils/password/pwd.py similarity index 98% rename from pkg/password/pwd.py rename to utils/password/pwd.py index 102ad5b..5043c81 100644 --- a/pkg/password/pwd.py +++ b/utils/password/pwd.py @@ -7,8 +7,8 @@ import pyotp from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired from pydantic import BaseModel, Field -from pkg.JWT.JWT import SECRET_KEY -from pkg.conf import appmeta +from utils.JWT.JWT import SECRET_KEY +from utils.conf import appmeta _ph = PasswordHasher()