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)