feat: 更新模型以支持 UUID,添加注册请求 DTO,重构用户注册逻辑
This commit is contained in:
@@ -37,7 +37,7 @@ async def router_directory_get(
|
||||
:param path: 目录路径
|
||||
:return: 目录内容
|
||||
"""
|
||||
folder = await Object.get_by_path(session, user.id, path or "/")
|
||||
folder = await Object.get_by_path(session, user.id, path or "/", user.username)
|
||||
|
||||
if not folder:
|
||||
raise HTTPException(status_code=404, detail="目录不存在")
|
||||
@@ -50,7 +50,7 @@ async def router_directory_get(
|
||||
|
||||
objects = [
|
||||
ObjectResponse(
|
||||
id=str(child.id),
|
||||
id=child.id,
|
||||
name=child.name,
|
||||
path=f"/{child.name}", # TODO: 完整路径
|
||||
thumb=False,
|
||||
@@ -63,7 +63,7 @@ async def router_directory_get(
|
||||
for child in children
|
||||
]
|
||||
|
||||
policy=PolicyResponse(
|
||||
policy_response = PolicyResponse(
|
||||
id=str(policy.id),
|
||||
name=policy.name,
|
||||
type=policy.type.value,
|
||||
@@ -71,9 +71,10 @@ async def router_directory_get(
|
||||
)
|
||||
|
||||
return DirectoryResponse(
|
||||
parent=str(folder.parent_id) if folder.parent_id else None,
|
||||
id=folder.id,
|
||||
parent=folder.parent_id,
|
||||
objects=objects,
|
||||
policy=policy,
|
||||
policy=policy_response,
|
||||
)
|
||||
|
||||
|
||||
@@ -91,26 +92,20 @@ async def router_directory_create(
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user: 当前登录用户
|
||||
:param request: 创建请求
|
||||
:param request: 创建请求(包含 parent_id UUID 和 name)
|
||||
:return: 创建结果
|
||||
"""
|
||||
path = request.path.strip()
|
||||
if not path or path == "/":
|
||||
raise HTTPException(status_code=400, detail="路径不能为空或根目录")
|
||||
# 验证目录名称
|
||||
name = request.name.strip()
|
||||
if not name:
|
||||
raise HTTPException(status_code=400, detail="目录名称不能为空")
|
||||
|
||||
# 解析路径
|
||||
if path.startswith("/"):
|
||||
path = path[1:]
|
||||
parts = [p for p in path.split("/") if p]
|
||||
if "/" in name or "\\" in name:
|
||||
raise HTTPException(status_code=400, detail="目录名称不能包含斜杠")
|
||||
|
||||
if not parts:
|
||||
raise HTTPException(status_code=400, detail="无效的目录路径")
|
||||
|
||||
new_folder_name = parts[-1]
|
||||
parent_path = "/" + "/".join(parts[:-1]) if len(parts) > 1 else "/"
|
||||
|
||||
parent = await Object.get_by_path(session, user.id, parent_path)
|
||||
if not parent:
|
||||
# 通过 UUID 获取父目录
|
||||
parent = await Object.get(session, Object.id == request.parent_id)
|
||||
if not parent or parent.owner_id != user.id:
|
||||
raise HTTPException(status_code=404, detail="父目录不存在")
|
||||
|
||||
if not parent.is_folder:
|
||||
@@ -121,25 +116,29 @@ async def router_directory_create(
|
||||
session,
|
||||
(Object.owner_id == user.id) &
|
||||
(Object.parent_id == parent.id) &
|
||||
(Object.name == new_folder_name)
|
||||
(Object.name == name)
|
||||
)
|
||||
if existing:
|
||||
raise HTTPException(status_code=409, detail="同名文件或目录已存在")
|
||||
|
||||
policy_id = request.policy_id if request.policy_id else parent.policy_id
|
||||
parent_id = parent.id # 在 save 前保存
|
||||
|
||||
new_folder = await Object(
|
||||
name=new_folder_name,
|
||||
new_folder = Object(
|
||||
name=name,
|
||||
type=ObjectType.FOLDER,
|
||||
owner_id=user.id,
|
||||
parent_id=parent.id,
|
||||
parent_id=parent_id,
|
||||
policy_id=policy_id,
|
||||
).save(session)
|
||||
)
|
||||
new_folder_id = new_folder.id # 在 save 前保存 UUID
|
||||
new_folder_name = new_folder.name
|
||||
await new_folder.save(session)
|
||||
|
||||
return response.ResponseModel(
|
||||
data={
|
||||
"id": new_folder.id,
|
||||
"name": new_folder.name,
|
||||
"path": f"{parent_path.rstrip('/')}/{new_folder_name}",
|
||||
"id": new_folder_id,
|
||||
"name": new_folder_name,
|
||||
"parent_id": parent_id,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from middleware.auth import SignRequired
|
||||
from typing import Annotated
|
||||
|
||||
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
|
||||
|
||||
object_router = APIRouter(
|
||||
@@ -7,41 +12,106 @@ object_router = APIRouter(
|
||||
tags=["object"]
|
||||
)
|
||||
|
||||
|
||||
@object_router.delete(
|
||||
path='/',
|
||||
summary='删除对象',
|
||||
description='Delete an object endpoint.',
|
||||
dependencies=[Depends(SignRequired)]
|
||||
description='删除一个或多个对象(文件或目录)',
|
||||
)
|
||||
def router_object_delete() -> ResponseModel:
|
||||
async def router_object_delete(
|
||||
session: SessionDep,
|
||||
user: Annotated[User, Depends(AuthRequired)],
|
||||
request: ObjectDeleteRequest,
|
||||
) -> ResponseModel:
|
||||
"""
|
||||
Delete an object endpoint.
|
||||
|
||||
Returns:
|
||||
ResponseModel: A model containing the response data for the object deletion.
|
||||
删除对象端点
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user: 当前登录用户
|
||||
:param request: 删除请求(包含待删除对象的UUID列表)
|
||||
:return: 删除结果
|
||||
"""
|
||||
pass
|
||||
deleted_count = 0
|
||||
|
||||
for obj_id in request.ids:
|
||||
obj = await Object.get(session, Object.id == obj_id)
|
||||
if obj and obj.owner_id == user.id:
|
||||
# TODO: 递归删除子对象(如果是目录)
|
||||
# TODO: 更新用户存储空间
|
||||
await obj.delete(session)
|
||||
deleted_count += 1
|
||||
|
||||
return ResponseModel(
|
||||
data={
|
||||
"deleted": deleted_count,
|
||||
"total": len(request.ids),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@object_router.patch(
|
||||
path='/',
|
||||
summary='移动对象',
|
||||
description='Move an object endpoint.',
|
||||
dependencies=[Depends(SignRequired)]
|
||||
description='移动一个或多个对象到目标目录',
|
||||
)
|
||||
def router_object_move() -> ResponseModel:
|
||||
async def router_object_move(
|
||||
session: SessionDep,
|
||||
user: Annotated[User, Depends(AuthRequired)],
|
||||
request: ObjectMoveRequest,
|
||||
) -> ResponseModel:
|
||||
"""
|
||||
Move an object endpoint.
|
||||
|
||||
Returns:
|
||||
ResponseModel: A model containing the response data for the object move.
|
||||
移动对象端点
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user: 当前登录用户
|
||||
:param request: 移动请求(包含源对象UUID列表和目标目录UUID)
|
||||
:return: 移动结果
|
||||
"""
|
||||
pass
|
||||
# 验证目标目录
|
||||
dst = await Object.get(session, Object.id == request.dst_id)
|
||||
if not dst or dst.owner_id != user.id:
|
||||
raise HTTPException(status_code=404, detail="目标目录不存在")
|
||||
|
||||
if not dst.is_folder:
|
||||
raise HTTPException(status_code=400, detail="目标不是有效文件夹")
|
||||
|
||||
moved_count = 0
|
||||
|
||||
for src_id in request.src_ids:
|
||||
src = await Object.get(session, Object.id == src_id)
|
||||
if not src or src.owner_id != user.id:
|
||||
continue
|
||||
|
||||
# 检查是否移动到自身或子目录(防止循环引用)
|
||||
if src.id == dst.id:
|
||||
continue
|
||||
|
||||
# 检查目标目录下是否存在同名对象
|
||||
existing = await Object.get(
|
||||
session,
|
||||
(Object.owner_id == user.id) &
|
||||
(Object.parent_id == dst.id) &
|
||||
(Object.name == src.name)
|
||||
)
|
||||
if existing:
|
||||
continue # 跳过重名对象
|
||||
|
||||
src.parent_id = dst.id
|
||||
await src.save(session)
|
||||
moved_count += 1
|
||||
|
||||
return ResponseModel(
|
||||
data={
|
||||
"moved": moved_count,
|
||||
"total": len(request.src_ids),
|
||||
}
|
||||
)
|
||||
|
||||
@object_router.post(
|
||||
path='/copy',
|
||||
summary='复制对象',
|
||||
description='Copy an object endpoint.',
|
||||
dependencies=[Depends(SignRequired)]
|
||||
dependencies=[Depends(AuthRequired)]
|
||||
)
|
||||
def router_object_copy() -> ResponseModel:
|
||||
"""
|
||||
@@ -56,7 +126,7 @@ def router_object_copy() -> ResponseModel:
|
||||
path='/rename',
|
||||
summary='重命名对象',
|
||||
description='Rename an object endpoint.',
|
||||
dependencies=[Depends(SignRequired)]
|
||||
dependencies=[Depends(AuthRequired)]
|
||||
)
|
||||
def router_object_rename() -> ResponseModel:
|
||||
"""
|
||||
@@ -71,7 +141,7 @@ def router_object_rename() -> ResponseModel:
|
||||
path='/property/{id}',
|
||||
summary='获取对象属性',
|
||||
description='Get object properties endpoint.',
|
||||
dependencies=[Depends(SignRequired)]
|
||||
dependencies=[Depends(AuthRequired)]
|
||||
)
|
||||
def router_object_property(id: str) -> ResponseModel:
|
||||
"""
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from typing import Annotated, Literal
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy import and_
|
||||
from webauthn import generate_registration_options
|
||||
from webauthn.helpers import options_to_json_dict
|
||||
import pyotp
|
||||
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
|
||||
|
||||
import models
|
||||
@@ -93,14 +93,77 @@ async def router_user_session(
|
||||
summary='用户注册',
|
||||
description='User registration endpoint.',
|
||||
)
|
||||
def router_user_register() -> models.response.ResponseModel:
|
||||
async def router_user_register(
|
||||
session: SessionDep,
|
||||
request: models.RegisterRequest,
|
||||
) -> models.response.ResponseModel:
|
||||
"""
|
||||
User registration endpoint.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing user registration information.
|
||||
用户注册端点
|
||||
|
||||
流程:
|
||||
1. 验证用户名唯一性
|
||||
2. 获取默认用户组
|
||||
3. 创建用户记录
|
||||
4. 创建以用户名命名的根目录
|
||||
|
||||
:param session: 数据库会话
|
||||
:param request: 注册请求
|
||||
:return: 注册结果
|
||||
:raises HTTPException 400: 用户名已存在
|
||||
:raises HTTPException 500: 默认用户组或存储策略不存在
|
||||
"""
|
||||
pass
|
||||
# 1. 验证用户名唯一性
|
||||
existing_user = await models.User.get(
|
||||
session,
|
||||
models.User.username == request.username
|
||||
)
|
||||
if existing_user:
|
||||
raise HTTPException(status_code=400, detail="用户名已存在")
|
||||
|
||||
# 2. 获取默认用户组(从设置中读取 UUID)
|
||||
default_group_setting: models.Setting | None = await models.Setting.get(
|
||||
session,
|
||||
and_(models.Setting.type == models.SettingsType.REGISTER, models.Setting.name == "default_group")
|
||||
)
|
||||
if default_group_setting is None or not default_group_setting.value:
|
||||
raise HTTPException(status_code=500, detail="默认用户组设置不存在")
|
||||
|
||||
default_group_id = UUID(default_group_setting.value)
|
||||
default_group = await models.Group.get(session, models.Group.id == default_group_id)
|
||||
if not default_group:
|
||||
raise HTTPException(status_code=500, detail="默认用户组不存在")
|
||||
|
||||
# 3. 创建用户
|
||||
hashed_password = Password.hash(request.password)
|
||||
new_user = models.User(
|
||||
username=request.username,
|
||||
password=hashed_password,
|
||||
group_id=default_group.id,
|
||||
)
|
||||
new_user_id = new_user.id # 在 save 前保存 UUID
|
||||
new_user_username = new_user.username
|
||||
await new_user.save(session)
|
||||
|
||||
# 4. 创建以用户名命名的根目录
|
||||
default_policy = await models.Policy.get(session, models.Policy.name == "本地存储")
|
||||
if not default_policy:
|
||||
raise HTTPException(status_code=500, detail="默认存储策略不存在")
|
||||
|
||||
await models.Object(
|
||||
name=new_user_username,
|
||||
type=models.ObjectType.FOLDER,
|
||||
owner_id=new_user_id,
|
||||
parent_id=None,
|
||||
policy_id=default_policy.id,
|
||||
).save(session)
|
||||
|
||||
return models.response.ResponseModel(
|
||||
data={
|
||||
"user_id": new_user_id,
|
||||
"username": new_user_username,
|
||||
},
|
||||
msg="注册成功",
|
||||
)
|
||||
|
||||
@user_router.post(
|
||||
path='/code',
|
||||
|
||||
Reference in New Issue
Block a user