- Implement PhysicalFile model to manage physical file references and reference counting. - Create Policy model with associated options and group links for storage policies. - Introduce Redeem and Report models for handling redeem codes and reports. - Add Settings model for site configuration and user settings management. - Develop Share model for sharing objects with unique codes and associated metadata. - Implement SourceLink model for managing download links associated with objects. - Create StoragePack model for managing user storage packages. - Add Tag model for user-defined tags with manual and automatic types. - Implement Task model for managing background tasks with status tracking. - Develop User model with comprehensive user management features including authentication. - Introduce UserAuthn model for managing WebAuthn credentials. - Create WebDAV model for managing WebDAV accounts associated with users.
203 lines
5.6 KiB
Python
203 lines
5.6 KiB
Python
from typing import Annotated
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
|
||
|
||
import sqlmodels
|
||
from middleware.auth import auth_required
|
||
from middleware.dependencies import SessionDep
|
||
from utils import JWT, Password, http_exceptions
|
||
|
||
user_settings_router = APIRouter(
|
||
prefix='/settings',
|
||
tags=["user", "user_settings"],
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
|
||
|
||
@user_settings_router.get(
|
||
path='/policies',
|
||
summary='获取用户可选存储策略',
|
||
description='Get user selectable storage policies.',
|
||
)
|
||
def router_user_settings_policies() -> sqlmodels.ResponseBase:
|
||
"""
|
||
Get user selectable storage policies.
|
||
|
||
Returns:
|
||
dict: A dictionary containing available storage policies for the user.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.get(
|
||
path='/nodes',
|
||
summary='获取用户可选节点',
|
||
description='Get user selectable nodes.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
def router_user_settings_nodes() -> sqlmodels.ResponseBase:
|
||
"""
|
||
Get user selectable nodes.
|
||
|
||
Returns:
|
||
dict: A dictionary containing available nodes for the user.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.get(
|
||
path='/tasks',
|
||
summary='任务队列',
|
||
description='Get user task queue.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
def router_user_settings_tasks() -> sqlmodels.ResponseBase:
|
||
"""
|
||
Get user task queue.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the user's task queue information.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.get(
|
||
path='/',
|
||
summary='获取当前用户设定',
|
||
description='Get current user settings.',
|
||
)
|
||
def router_user_settings(
|
||
user: Annotated[sqlmodels.user.User, Depends(auth_required)],
|
||
) -> sqlmodels.UserSettingResponse:
|
||
"""
|
||
Get current user settings.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the current user settings.
|
||
"""
|
||
return sqlmodels.UserSettingResponse(
|
||
id=user.id,
|
||
email=user.email,
|
||
nickname=user.nickname,
|
||
created_at=user.created_at,
|
||
group_name=user.group.name,
|
||
language=user.language,
|
||
timezone=user.timezone,
|
||
group_expires=user.group_expires,
|
||
two_factor=user.two_factor is not None,
|
||
)
|
||
|
||
|
||
@user_settings_router.post(
|
||
path='/avatar',
|
||
summary='从文件上传头像',
|
||
description='Upload user avatar from file.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
def router_user_settings_avatar() -> sqlmodels.ResponseBase:
|
||
"""
|
||
Upload user avatar from file.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the result of the avatar upload.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.put(
|
||
path='/avatar',
|
||
summary='设定为Gravatar头像',
|
||
description='Set user avatar to Gravatar.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
def router_user_settings_avatar_gravatar() -> sqlmodels.ResponseBase:
|
||
"""
|
||
Set user avatar to Gravatar.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the result of setting the Gravatar avatar.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.patch(
|
||
path='/{option}',
|
||
summary='更新用户设定',
|
||
description='Update user settings.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
def router_user_settings_patch(option: str) -> sqlmodels.ResponseBase:
|
||
"""
|
||
Update user settings.
|
||
|
||
Args:
|
||
option (str): The setting option to update.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the result of the settings update.
|
||
"""
|
||
http_exceptions.raise_not_implemented()
|
||
|
||
|
||
@user_settings_router.get(
|
||
path='/2fa',
|
||
summary='获取两步验证初始化信息',
|
||
description='Get two-factor authentication initialization information.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
async def router_user_settings_2fa(
|
||
user: Annotated[sqlmodels.user.User, Depends(auth_required)],
|
||
) -> sqlmodels.ResponseBase:
|
||
"""
|
||
Get two-factor authentication initialization information.
|
||
|
||
Returns:
|
||
dict: A dictionary containing two-factor authentication setup information.
|
||
"""
|
||
|
||
return sqlmodels.ResponseBase(
|
||
data=await Password.generate_totp(user.email)
|
||
)
|
||
|
||
|
||
@user_settings_router.post(
|
||
path='/2fa',
|
||
summary='启用两步验证',
|
||
description='Enable two-factor authentication.',
|
||
dependencies=[Depends(auth_required)],
|
||
)
|
||
async def router_user_settings_2fa_enable(
|
||
session: SessionDep,
|
||
user: Annotated[sqlmodels.user.User, Depends(auth_required)],
|
||
setup_token: str,
|
||
code: str,
|
||
) -> sqlmodels.ResponseBase:
|
||
"""
|
||
Enable two-factor authentication for the user.
|
||
|
||
Returns:
|
||
dict: A dictionary containing the result of enabling two-factor authentication.
|
||
"""
|
||
|
||
serializer = URLSafeTimedSerializer(JWT.SECRET_KEY)
|
||
|
||
try:
|
||
# 1. 解包 Token,设置有效期(例如 600秒)
|
||
secret = serializer.loads(setup_token, salt="2fa-setup-salt", max_age=600)
|
||
except SignatureExpired:
|
||
raise HTTPException(status_code=400, detail="Setup session expired")
|
||
except BadSignature:
|
||
raise HTTPException(status_code=400, detail="Invalid token")
|
||
|
||
# 2. 验证用户输入的 6 位验证码
|
||
if not Password.verify_totp(secret, code):
|
||
raise HTTPException(status_code=400, detail="Invalid OTP code")
|
||
|
||
# 3. 将 secret 存储到用户的数据库记录中,启用 2FA
|
||
user.two_factor = secret
|
||
user = await user.save(session)
|
||
|
||
return sqlmodels.ResponseBase(
|
||
data={"message": "Two-factor authentication enabled successfully"}
|
||
) |