nex_docus/backend/app/api/v1/preview.py

168 lines
5.3 KiB
Python
Raw Normal View History

2025-12-20 11:18:59 +00:00
"""
2025-12-29 12:53:50 +00:00
项目预览相关 API支持公开和私密项目
2025-12-20 11:18:59 +00:00
"""
from fastapi import APIRouter, Depends, HTTPException, Header
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import Optional
from app.core.database import get_db
2025-12-29 12:53:50 +00:00
from app.core.deps import get_current_user_optional
from app.models.project import Project, ProjectMember
from app.models.user import User
2025-12-20 11:18:59 +00:00
from app.schemas.response import success_response
from app.services.storage import storage_service
router = APIRouter()
2025-12-29 12:53:50 +00:00
async def check_preview_access(
project: Project,
current_user: Optional[User],
db: AsyncSession
):
"""检查预览访问权限"""
# 公开项目:任何人都可以访问
if project.is_public == 1:
return True
# 私密项目:必须是项目成员
if not current_user:
raise HTTPException(status_code=401, detail="私密项目需要登录才能访问")
# 检查是否是项目所有者
if project.owner_id == current_user.id:
return True
# 检查是否是项目成员
member_result = await db.execute(
select(ProjectMember).where(
ProjectMember.project_id == project.id,
ProjectMember.user_id == current_user.id
)
)
member = member_result.scalar_one_or_none()
if not member:
raise HTTPException(status_code=403, detail="无权访问该私密项目")
return True
2025-12-20 11:18:59 +00:00
@router.get("/{project_id}/info", response_model=dict)
async def get_preview_info(
project_id: int,
2025-12-29 12:53:50 +00:00
current_user: Optional[User] = Depends(get_current_user_optional),
2025-12-20 11:18:59 +00:00
db: AsyncSession = Depends(get_db)
):
2025-12-29 12:53:50 +00:00
"""获取预览项目基本信息"""
2025-12-20 11:18:59 +00:00
# 查询项目
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
raise HTTPException(status_code=404, detail="项目不存在")
2025-12-29 12:53:50 +00:00
# 检查访问权限
await check_preview_access(project, current_user, db)
2025-12-20 11:18:59 +00:00
# 返回基本信息
info = {
"id": project.id,
"name": project.name,
"description": project.description,
2025-12-29 12:53:50 +00:00
"is_public": project.is_public,
2025-12-20 11:18:59 +00:00
"has_password": bool(project.access_pass),
}
return success_response(data=info)
@router.post("/{project_id}/verify", response_model=dict)
async def verify_access_password(
project_id: int,
password: str = Header(..., alias="X-Access-Password"),
2025-12-29 12:53:50 +00:00
current_user: Optional[User] = Depends(get_current_user_optional),
2025-12-20 11:18:59 +00:00
db: AsyncSession = Depends(get_db)
):
"""验证访问密码"""
# 查询项目
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
raise HTTPException(status_code=404, detail="项目不存在")
2025-12-29 12:53:50 +00:00
# 检查访问权限
await check_preview_access(project, current_user, db)
2025-12-20 11:18:59 +00:00
# 验证密码
if not project.access_pass:
return success_response(message="该项目无需密码访问")
if project.access_pass != password:
raise HTTPException(status_code=403, detail="访问密码错误")
return success_response(message="验证成功")
@router.get("/{project_id}/tree", response_model=dict)
async def get_preview_tree(
project_id: int,
password: Optional[str] = Header(None, alias="X-Access-Password"),
2025-12-29 12:53:50 +00:00
current_user: Optional[User] = Depends(get_current_user_optional),
2025-12-20 11:18:59 +00:00
db: AsyncSession = Depends(get_db)
):
2025-12-29 12:53:50 +00:00
"""获取预览项目的文档树"""
2025-12-20 11:18:59 +00:00
# 查询项目
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
raise HTTPException(status_code=404, detail="项目不存在")
2025-12-29 12:53:50 +00:00
# 检查访问权限
await check_preview_access(project, current_user, db)
2025-12-20 11:18:59 +00:00
# 如果设置了密码,需要验证
if project.access_pass:
if not password or project.access_pass != password:
raise HTTPException(status_code=403, detail="需要提供正确的访问密码")
# 获取文档树
project_path = storage_service.get_secure_path(project.storage_key)
tree = storage_service.generate_tree(project_path)
return success_response(data=tree)
@router.get("/{project_id}/file", response_model=dict)
async def get_preview_file(
project_id: int,
path: str,
password: Optional[str] = Header(None, alias="X-Access-Password"),
2025-12-29 12:53:50 +00:00
current_user: Optional[User] = Depends(get_current_user_optional),
2025-12-20 11:18:59 +00:00
db: AsyncSession = Depends(get_db)
):
2025-12-29 12:53:50 +00:00
"""获取预览项目的文件内容"""
2025-12-20 11:18:59 +00:00
# 查询项目
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
raise HTTPException(status_code=404, detail="项目不存在")
2025-12-29 12:53:50 +00:00
# 检查访问权限
await check_preview_access(project, current_user, db)
2025-12-20 11:18:59 +00:00
# 如果设置了密码,需要验证
if project.access_pass:
if not password or project.access_pass != password:
raise HTTPException(status_code=403, detail="需要提供正确的访问密码")
# 获取文件内容
file_path = storage_service.get_secure_path(project.storage_key, path)
content = await storage_service.read_file(file_path)
return success_response(data={"content": content})