Files
disknext/routers/api/v1/trash/__init__.py
于小丘 4c1b7a8aad feat: add theme preset system with admin CRUD, public listing, and user theme settings
- Add ChromaticColor (17 Tailwind colors) and NeutralColor (5 grays) enums
- Add ThemePreset table with flat color columns and unique name constraint
- Add admin theme endpoints (CRUD + set default) at /api/v1/admin/theme
- Add public theme listing at /api/v1/site/themes
- Add user theme settings (PATCH /theme) with color snapshot on User model
- User.color_* columns store per-user overrides; fallback to default preset then builtin
- Initialize default theme preset in migration
- Remove legacy defaultTheme/themes settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:34:41 +08:00

162 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
回收站路由
提供回收站管理功能:列出、恢复、永久删除、清空。
路由前缀:/trash
"""
from typing import Annotated
from fastapi import APIRouter, Depends
from loguru import logger as l
from middleware.auth import auth_required
from middleware.dependencies import SessionDep
from sqlmodels import Object, User
from sqlmodels.object import TrashDeleteRequest, TrashItemResponse, TrashRestoreRequest
from service.storage.object import (
permanently_delete_objects,
restore_objects,
soft_delete_objects,
)
trash_router = APIRouter(
prefix="/trash",
tags=["trash"],
)
@trash_router.get(
path='/',
summary='列出回收站内容',
description='获取当前用户回收站中的所有顶层对象。',
)
async def router_trash_list(
session: SessionDep,
user: Annotated[User, Depends(auth_required)],
) -> list[TrashItemResponse]:
"""
列出回收站内容
认证:需要 JWT token
返回回收站中被直接删除的顶层对象列表,
不包含其子对象(子对象在恢复/永久删除时会随顶层对象一起处理)。
"""
items = await Object.get_trash_items(session, user.id)
return [
TrashItemResponse(
id=item.id,
name=item.name,
type=item.type,
size=item.size,
deleted_at=item.deleted_at,
original_parent_id=item.deleted_original_parent_id,
)
for item in items
]
@trash_router.patch(
path='/restore',
summary='恢复对象',
description='从回收站恢复一个或多个对象到原位置。如果原位置不存在则恢复到根目录。',
status_code=204,
)
async def router_trash_restore(
session: SessionDep,
user: Annotated[User, Depends(auth_required)],
request: TrashRestoreRequest,
) -> None:
"""
从回收站恢复对象
认证:需要 JWT token
流程:
1. 验证对象存在且在回收站中deleted_at IS NOT NULL
2. 检查原父目录是否存在且未删除
3. 存在 → 恢复到原位置;不存在 → 恢复到根目录
4. 处理同名冲突(自动重命名)
5. 清除 deleted_at 和 deleted_original_parent_id
"""
user_id = user.id
objects_to_restore: list[Object] = []
for obj_id in request.ids:
obj = await Object.get(
session,
(Object.id == obj_id) & (Object.owner_id == user_id) & (Object.deleted_at != None)
)
if obj:
objects_to_restore.append(obj)
if objects_to_restore:
restored_count = await restore_objects(session, objects_to_restore, user_id)
l.info(f"用户 {user_id} 从回收站恢复了 {restored_count} 个对象")
@trash_router.delete(
path='/',
summary='永久删除对象',
description='永久删除回收站中的指定对象,包括物理文件和数据库记录。',
status_code=204,
)
async def router_trash_delete(
session: SessionDep,
user: Annotated[User, Depends(auth_required)],
request: TrashDeleteRequest,
) -> None:
"""
永久删除回收站中的对象
认证:需要 JWT token
流程:
1. 验证对象存在且在回收站中
2. BFS 收集所有子文件的 PhysicalFile
3. 处理引用计数,引用为 0 时物理删除文件
4. 硬删除根 ObjectCASCADE 清理子对象)
5. 更新用户存储配额
"""
user_id = user.id
objects_to_delete: list[Object] = []
for obj_id in request.ids:
obj = await Object.get(
session,
(Object.id == obj_id) & (Object.owner_id == user_id) & (Object.deleted_at != None)
)
if obj:
objects_to_delete.append(obj)
if objects_to_delete:
deleted_count = await permanently_delete_objects(session, objects_to_delete, user_id)
l.info(f"用户 {user_id} 永久删除了 {deleted_count} 个对象")
@trash_router.delete(
path='/empty',
summary='清空回收站',
description='永久删除回收站中的所有对象。',
status_code=204,
)
async def router_trash_empty(
session: SessionDep,
user: Annotated[User, Depends(auth_required)],
) -> None:
"""
清空回收站
认证:需要 JWT token
获取回收站中所有顶层对象,逐个执行永久删除。
"""
user_id = user.id
trash_items = await Object.get_trash_items(session, user_id)
if trash_items:
deleted_count = await permanently_delete_objects(session, trash_items, user_id)
l.info(f"用户 {user_id} 清空回收站,共删除 {deleted_count} 个对象")