Files
JARVIS/backend/app/routers/remote_mount.py

131 lines
4.3 KiB
Python
Raw Normal View History

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import and_, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models.remote_mount import RemoteMount
from app.models.user import User
from app.routers.auth import get_current_user
from app.schemas.remote_mount import (
RemoteMountCreate,
RemoteMountOut,
RemoteMountTreeOut,
RemoteNodeOut,
RemoteSyncRequest,
RemoteSyncResultOut,
)
from app.services.remote_sync_service import RemoteSyncService
from app.services.secret_service import encrypt_secret
from app.services.webdav_service import WebDavNode, WebDavService
router = APIRouter(prefix="/api/remote-mounts", tags=["远程挂载"])
def _to_node_out(node: WebDavNode) -> RemoteNodeOut:
return RemoteNodeOut(
path=node.path,
name=node.name,
is_dir=node.is_dir,
size=node.size,
modified_at=node.modified_at,
etag=node.etag,
children=[_to_node_out(child) for child in node.children],
)
@router.get("", response_model=list[RemoteMountOut])
async def list_remote_mounts(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(
select(RemoteMount).where(RemoteMount.user_id == current_user.id).order_by(RemoteMount.created_at.desc())
)
return list(result.scalars().all())
@router.post("", response_model=RemoteMountOut, status_code=status.HTTP_201_CREATED)
async def create_remote_mount(
payload: RemoteMountCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
existing = await db.execute(
select(RemoteMount).where(and_(RemoteMount.user_id == current_user.id, RemoteMount.name == payload.name))
)
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="同名远程挂载已存在")
mount = RemoteMount(
user_id=current_user.id,
name=payload.name,
mount_type="webdav",
base_url=str(payload.base_url),
username=payload.username,
password_encrypted=encrypt_secret(payload.password),
root_path=payload.root_path,
is_active=True,
)
try:
await WebDavService(mount).list_directory(payload.root_path)
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=f"WebDAV 连接失败: {exc}") from exc
db.add(mount)
await db.commit()
await db.refresh(mount)
return mount
async def _get_user_mount(db: AsyncSession, user_id: str, mount_id: str) -> RemoteMount:
result = await db.execute(
select(RemoteMount).where(and_(RemoteMount.id == mount_id, RemoteMount.user_id == user_id))
)
mount = result.scalar_one_or_none()
if mount is None:
raise HTTPException(status_code=404, detail="远程挂载不存在")
return mount
@router.get("/{mount_id}/tree", response_model=RemoteMountTreeOut)
async def get_remote_tree(
mount_id: str,
path: str | None = None,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
mount = await _get_user_mount(db, current_user.id, mount_id)
try:
nodes = await WebDavService(mount).list_tree(path or mount.root_path)
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=f"远程目录读取失败: {exc}") from exc
return RemoteMountTreeOut(
mount_id=mount.id,
root_path=path or mount.root_path,
nodes=[_to_node_out(node) for node in nodes],
)
@router.post("/{mount_id}/sync", response_model=RemoteSyncResultOut)
async def sync_remote_mount(
mount_id: str,
payload: RemoteSyncRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
mount = await _get_user_mount(db, current_user.id, mount_id)
try:
result = await RemoteSyncService(db, current_user.id).sync_remote_path(
mount,
payload.remote_path,
payload.local_folder_id,
payload.mode,
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=500, detail=f"远程同步失败: {exc}") from exc
return RemoteSyncResultOut(**result)