import os import shutil from pathlib import Path from fastapi import APIRouter, UploadFile, File from fastapi.responses import FileResponse from config import VOLUME_ROOT router = APIRouter() BASE = Path(VOLUME_ROOT) def _safe_path(path: str) -> Path: target = (BASE / path).resolve() if not str(target).startswith(str(BASE.resolve())): raise ValueError("forbidden") return target @router.get("/browse") def browse(path: str = ""): target = _safe_path(path) entries = [] for item in sorted(target.iterdir()): try: st = item.stat() entries.append({"name": item.name, "is_dir": item.is_dir(), "size": st.st_size if item.is_file() else 0}) except PermissionError: continue return {"path": str(target.relative_to(BASE)), "entries": entries} @router.get("/download") def download(path: str): return FileResponse(_safe_path(path)) @router.post("/upload") async def upload(path: str, file: UploadFile = File(...)): target = _safe_path(path) / file.filename with open(target, "wb") as f: shutil.copyfileobj(file.file, f) return {"ok": True} @router.delete("/delete") def delete(path: str): target = _safe_path(path) if target.is_dir(): shutil.rmtree(target) else: target.unlink() return {"ok": True}