优化部分代码实现

This commit is contained in:
2025-08-28 02:23:26 +08:00
parent 9f93f6040b
commit f3a5ae9c40
5 changed files with 63 additions and 87 deletions

View File

@@ -1,5 +1,6 @@
from .setting import Setting from .setting import Setting
from pkg.conf.appmeta import BackendVersion from pkg.conf.appmeta import BackendVersion
from .response import ThemeModel
from pkg.password.pwd import Password from pkg.password.pwd import Password
from pkg.log import log from pkg.log import log
@@ -71,7 +72,7 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
Setting(name="hot_share_num", value="10", type="share"), Setting(name="hot_share_num", value="10", type="share"),
Setting(name="gravatar_server", value="https://www.gravatar.com/", type="avatar"), Setting(name="gravatar_server", value="https://www.gravatar.com/", type="avatar"),
Setting(name="defaultTheme", value="#3f51b5", type="basic"), Setting(name="defaultTheme", value="#3f51b5", type="basic"),
Setting(name="themes", value={"#3f51b5":{"palette":{"primary":{"main":"#3f51b5"},"secondary":{"main":"#f50057"}}},"#2196f3":{"palette":{"primary":{"main":"#2196f3"},"secondary":{"main":"#FFC107"}}},"#673AB7":{"palette":{"primary":{"main":"#673AB7"},"secondary":{"main":"#2196F3"}}},"#E91E63":{"palette":{"primary":{"main":"#E91E63"},"secondary":{"main":"#42A5F5","contrastText":"#fff"}}},"#FF5722":{"palette":{"primary":{"main":"#FF5722"},"secondary":{"main":"#3F51B5"}}},"#FFC107":{"palette":{"primary":{"main":"#FFC107"},"secondary":{"main":"#26C6DA"}}},"#8BC34A":{"palette":{"primary":{"main":"#8BC34A","contrastText":"#fff"},"secondary":{"main":"#FF8A65","contrastText":"#fff"}}},"#009688":{"palette":{"primary":{"main":"#009688"},"secondary":{"main":"#4DD0E1","contrastText":"#fff"}}},"#607D8B":{"palette":{"primary":{"main":"#607D8B"},"secondary":{"main":"#F06292"}}},"#795548":{"palette":{"primary":{"main":"#795548"},"secondary":{"main":"#4CAF50","contrastText":"#fff"}}}}, type="basic"), Setting(name="themes", value=ThemeModel().model_dump(), type="basic"),
Setting(name="aria2_token", value="", type="aria2"), Setting(name="aria2_token", value="", type="aria2"),
Setting(name="aria2_rpcurl", value="", type="aria2"), Setting(name="aria2_rpcurl", value="", type="aria2"),
Setting(name="aria2_temp_path", value="", type="aria2"), Setting(name="aria2_temp_path", value="", type="aria2"),

View File

@@ -4,18 +4,41 @@ from datetime import datetime, timezone
from uuid import uuid4 from uuid import uuid4
class ResponseModel(BaseModel): class ResponseModel(BaseModel):
'''
默认响应模型
'''
code: int = Field(default=0, description="系统内部状态码, 0表示成功其他表示失败", lt=60000, gt=0) code: int = Field(default=0, description="系统内部状态码, 0表示成功其他表示失败", lt=60000, gt=0)
data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据") data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据")
msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示") msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示")
instance_id: str = Field(default_factory=lambda: str(uuid4()), description="实例ID用于标识请求的唯一性") instance_id: str = Field(default_factory=lambda: str(uuid4()), description="实例ID用于标识请求的唯一性")
class ThemeModel(BaseModel):
'''
主题模型
'''
primary: str = Field(default="#3f51b5", description="Primary color")
secondary: str = Field(default="#f50057", description="Secondary color")
accent: str = Field(default="#9c27b0", description="Accent color")
dark: str = Field(default="#1d1d1d", description="Dark color")
dark_page: str = Field(default="#121212", description="Dark page color")
positive: str = Field(default="#21ba45", description="Positive color")
negative: str = Field(default="#c10015", description="Negative color")
info: str = Field(default="#31ccec", description="Info color")
warning: str = Field(default="#f2c037", description="Warning color")
class TokenModel(BaseModel): class TokenModel(BaseModel):
'''
访问令牌模型
'''
access_expires: datetime = Field(default=None, description="访问令牌的过期时间") access_expires: datetime = Field(default=None, description="访问令牌的过期时间")
access_token: str = Field(default=None, description="访问令牌") access_token: str = Field(default=None, description="访问令牌")
refresh_expires: datetime = Field(default=None, description="刷新令牌的过期时间") refresh_expires: datetime = Field(default=None, description="刷新令牌的过期时间")
refresh_token: str = Field(default=None, description="刷新令牌") refresh_token: str = Field(default=None, description="刷新令牌")
class groupModel(BaseModel): class groupModel(BaseModel):
'''
用户组模型
'''
id: int = Field(default=None, description="用户组ID") id: int = Field(default=None, description="用户组ID")
name: str = Field(default=None, description="用户组名称") name: str = Field(default=None, description="用户组名称")
allowShare: bool = Field(default=False, description="是否允许分享") allowShare: bool = Field(default=False, description="是否允许分享")
@@ -32,19 +55,25 @@ class groupModel(BaseModel):
advanceDelete: bool = Field(default=False, description="是否允许高级删除") advanceDelete: bool = Field(default=False, description="是否允许高级删除")
class userModel(BaseModel): class userModel(BaseModel):
'''
用户模型
'''
id: int = Field(default=None, description="用户ID") id: int = Field(default=None, description="用户ID")
username: str = Field(default=None, description="用户名") username: str = Field(default=None, description="用户名")
nickname: str = Field(default=None, description="用户昵称") nickname: str = Field(default=None, description="用户昵称")
status: int = Field(default=0, description="用户状态") status: int = Field(default=0, description="用户状态")
avatar: Literal['default', 'gravatar', 'file'] = Field(default='default', description="头像类型") avatar: Literal['default', 'gravatar', 'file'] = Field(default='default', description="头像类型")
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), description="用户创建时间") created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), description="用户创建时间")
preferred_theme: str = Field(default="#607D8B", description="用户首选主题") preferred_theme: ThemeModel = Field(default_factory=ThemeModel, description="用户首选主题")
score: int = Field(default=0, description="用户积分") score: int = Field(default=0, description="用户积分")
anonymous: bool = Field(default=False, description="是否为匿名用户") anonymous: bool = Field(default=False, description="是否为匿名用户")
group: groupModel = Field(default_factory=None, description="用户所属用户组") group: groupModel = Field(default_factory=None, description="用户所属用户组")
tags: list = Field(default_factory=list, description="用户标签列表") tags: list = Field(default_factory=list, description="用户标签列表")
class SiteConfigModel(ResponseModel): class SiteConfigModel(ResponseModel):
'''
站点配置模型
'''
title: str = Field(default="DiskNext", description="网站标题") title: str = Field(default="DiskNext", description="网站标题")
themes: dict = Field(default_factory=dict, description="网站主题配置") themes: dict = Field(default_factory=dict, description="网站主题配置")
default_theme: str = Field(default="default", description="默认主题RGB色号") default_theme: str = Field(default="default", description="默认主题RGB色号")
@@ -56,13 +85,19 @@ class SiteConfigModel(ResponseModel):
captcha_key: Optional[str] = Field(default=None, description="验证码密钥") captcha_key: Optional[str] = Field(default=None, description="验证码密钥")
class AuthnModel(BaseModel): class AuthnModel(BaseModel):
'''
WebAuthn模型
'''
id: str = Field(default=None, description="ID") id: str = Field(default=None, description="ID")
fingerprint: str = Field(default=None, description="指纹") fingerprint: str = Field(default=None, description="指纹")
class UserSettingModel(BaseModel): class UserSettingModel(BaseModel):
'''
用户设置模型
'''
authn: Optional[AuthnModel] = Field(default=None, description="认证信息") authn: Optional[AuthnModel] = Field(default=None, description="认证信息")
group_expires: Optional[datetime] = Field(default=None, description="用户组过期时间") group_expires: Optional[datetime] = Field(default=None, description="用户组过期时间")
prefer_theme: str = Field(default="#607D8B", description="用户首选主题") prefer_theme: str = Field(default="#5898d4", description="用户首选主题")
qq: str | bool = Field(default=False, description="QQ号") qq: str | bool = Field(default=False, description="QQ号")
themes: dict = Field(default_factory=dict, description="用户主题配置") themes: dict = Field(default_factory=dict, description="用户主题配置")
two_factor: bool = Field(default=False, description="是否启用两步验证") two_factor: bool = Field(default=False, description="是否启用两步验证")

View File

@@ -51,48 +51,6 @@ async def router_callback_github(
code: str = Query(description="The token received from GitHub for authentication.")) -> PlainTextResponse: code: str = Query(description="The token received from GitHub for authentication.")) -> PlainTextResponse:
""" """
GitHub OAuth 回调处理 GitHub OAuth 回调处理
- Github 成功响应:
- JWT: {"access_token": "gho_xxxxxxxx", "token_type": "bearer", "scope": ""}
- User Info:{
"code": "grfessg1312432313421fdgs",
"user_data": {
"login": "Yuerchu",
"id": 114514,
"node_id": "xxxxx",
"avatar_url": "https://avatars.githubusercontent.com/u/114514?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Yuerchu",
"html_url": "https://github.com/Yuerchu",
"followers_url": "https://api.github.com/users/Yuerchu/followers",
"following_url": "https://api.github.com/users/Yuerchu/following{/other_user}",
"gists_url": "https://api.github.com/users/Yuerchu/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Yuerchu/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Yuerchu/subscriptions",
"organizations_url": "https://api.github.com/users/Yuerchu/orgs",
"repos_url": "https://api.github.com/users/Yuerchu/repos",
"events_url": "https://api.github.com/users/Yuerchu/events{/privacy}",
"received_events_url": "https://api.github.com/users/Yuerchu/received_events",
"type": "User",
"user_view_type": "public",
"site_admin": false,
"name": "于小丘",
"company": null,
"blog": "https://www.yxqi.cn",
"location": "ChangSha, HuNan, China",
"email": "admin@yuxiaoqiu.cn",
"hireable": null,
"bio": null,
"twitter_username": null,
"notification_email": "admin@yuxiaoqiu.cn",
"public_repos": 17,
"public_gists": 0,
"followers": 8,
"following": 8,
"created_at": "2019-04-13T11:17:33Z",
"updated_at": "2025-08-20T03:03:16Z"
}
}
- 错误响应示例: - 错误响应示例:
- { - {
'error': 'bad_verification_code', 'error': 'bad_verification_code',

View File

@@ -30,18 +30,21 @@ async def router_user_session(
username = form_data.username username = form_data.username
password = form_data.password password = form_data.password
user = await service.user.Login(username=username, password=password) is_login, detail = await service.user.Login(username=username, password=password)
if user is None: if not is_login:
if detail in ["User not found", "Incorrect password"]:
raise HTTPException(status_code=400, detail="Invalid username or password") raise HTTPException(status_code=400, detail="Invalid username or password")
elif user == 1: elif detail == "Need to complete registration":
raise HTTPException(status_code=400, detail="User account is not fully registered") raise HTTPException(status_code=400, detail="User account is not fully registered")
elif user == 2: elif detail == "Account is banned":
raise HTTPException(status_code=403, detail="User account is banned") raise HTTPException(status_code=403, detail="User account is banned")
elif isinstance(user, TokenModel):
return user
else: else:
log.error(f"Unexpected return type from login service: {type(user)}") raise HTTPException(status_code=500, detail="Internal server error during login")
if isinstance(detail, TokenModel):
return detail
else:
log.error(f"Unexpected return type from login service: {type(detail)}")
raise HTTPException(status_code=500, detail="Internal server error during login") raise HTTPException(status_code=500, detail="Internal server error during login")
@user_router.post( @user_router.post(
@@ -73,13 +76,13 @@ def router_user_2fa() -> ResponseModel:
pass pass
@user_router.post( @user_router.post(
path='/reset', path='/code',
summary='发送密码重设邮件', summary='发送验证码邮件',
description='Send a password reset email.', description='Send a verification code email.',
) )
def router_user_reset() -> ResponseModel: def router_user_email_code() -> ResponseModel:
""" """
Send a password reset email. Send a pas
Returns: Returns:
dict: A dictionary containing information about the password reset email. dict: A dictionary containing information about the password reset email.
@@ -118,27 +121,6 @@ def router_user_qq() -> ResponseModel:
""" """
pass pass
@deprecated(
version="0.0.1",
reason="邮件中带链接的激活易使得被收件服务器误判为垃圾邮件,新版更换为验证码方式"
)
@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.
"""
pass
@user_router.get( @user_router.get(
path='authn/{username}', path='authn/{username}',
summary='WebAuthn登录初始化', summary='WebAuthn登录初始化',

View File

@@ -9,7 +9,7 @@ async def Login(
password: str, password: str,
captcha: Optional[str] = None, captcha: Optional[str] = None,
twoFaCode: Optional[str] = None twoFaCode: Optional[str] = None
) -> TokenModel | int | None: ) -> tuple[bool, TokenModel | str]:
""" """
根据账号密码进行登录。 根据账号密码进行登录。
@@ -41,20 +41,20 @@ async def Login(
if not user: if not user:
log.debug(f"Cannot find user with email: {username}") log.debug(f"Cannot find user with email: {username}")
return None return False, "User not found"
# 验证密码是否正确 # 验证密码是否正确
if not Password.verify(user.password, password): if not Password.verify(user.password, password):
log.debug(f"Password verification failed for user: {username}") log.debug(f"Password verification failed for user: {username}")
return None return False, "Incorrect password"
# 验证用户是否可登录 # 验证用户是否可登录
if user.status == 1: if user.status == 1:
# 未完成注册 # 未完成注册
return 1 return False, "Need to complete registration"
elif user.status == 2: elif user.status == 2:
# 账号已被封禁 # 账号已被封禁
return 2 return False, "Account is banned"
# 创建令牌 # 创建令牌
from pkg.JWT.JWT import create_access_token, create_refresh_token from pkg.JWT.JWT import create_access_token, create_refresh_token
@@ -62,7 +62,7 @@ async def Login(
access_token, access_expire = create_access_token(data={'sub': user.email}) access_token, access_expire = create_access_token(data={'sub': user.email})
refresh_token, refresh_expire = create_refresh_token(data={'sub': user.email}) refresh_token, refresh_expire = create_refresh_token(data={'sub': user.email})
return TokenModel( return True, TokenModel(
access_token=access_token, access_token=access_token,
access_expires=access_expire, access_expires=access_expire,
refresh_token=refresh_token, refresh_token=refresh_token,