From 93e92c5ff5b15a1e46ffd1ffaaf604a90b315b9c Mon Sep 17 00:00:00 2001 From: Thierry Date: Thu, 26 Mar 2026 11:43:20 +0100 Subject: [PATCH] Upload files to "backend/routes" --- backend/routes/admin.py | 92 ++++++++++++++++++++++++++++++++++++++++ backend/routes/upload.py | 47 ++++++++++++++++++++ backend/routes/viewer.py | 23 ++++++++++ 3 files changed, 162 insertions(+) create mode 100644 backend/routes/admin.py create mode 100644 backend/routes/upload.py create mode 100644 backend/routes/viewer.py diff --git a/backend/routes/admin.py b/backend/routes/admin.py new file mode 100644 index 0000000..2b832ba --- /dev/null +++ b/backend/routes/admin.py @@ -0,0 +1,92 @@ +from fastapi import APIRouter, HTTPException +from fastapi.responses import JSONResponse +from config import EPT_DIR, UPLOADS_DIR +from services.manifest import read_manifest +from services.converter import ENTWINE_AVAILABLE, ENTWINE_PATH +import shutil + +router = APIRouter() + + +@router.get("/list") +def list_pointclouds(): + pointclouds = [] + + for item in sorted(EPT_DIR.iterdir(), key=lambda x: x.stat().st_ctime, reverse=True): + if item.is_dir(): + manifest = read_manifest(item) + + total_size = 0 + file_count = 0 + for f in item.rglob("*"): + if f.is_file(): + total_size += f.stat().st_size + file_count += 1 + + if file_count > 0: + pointclouds.append({ + "id": item.name, + "size_mb": round(total_size / (1024 * 1024), 2), + "file_count": file_count, + "manifest": manifest, + "created": item.stat().st_ctime, + }) + + return {"pointclouds": pointclouds} + + +@router.get("/debug/{pc_id}") +def debug(pc_id: str): + out_dir = EPT_DIR / pc_id + if not out_dir.exists(): + raise HTTPException(status_code=404, detail=f"ID {pc_id} non trouvé") + + manifest = read_manifest(out_dir) + + files = [] + total_size = 0 + for p in out_dir.rglob("*"): + if p.is_file(): + size = p.stat().st_size + total_size += size + files.append({ + "path": str(p.relative_to(out_dir)), + "size_mb": round(size / (1024 * 1024), 2), + }) + + entry_file = manifest.get("entry_file") + entry_exists = False + if entry_file: + entry_exists = (EPT_DIR / entry_file).exists() + + return { + "pc_id": pc_id, + "exists": True, + "manifest": manifest, + "entry_exists": entry_exists, + "stats": { + "total_files": len(files), + "total_size_mb": round(total_size / (1024 * 1024), 2), + }, + "files": sorted(files, key=lambda x: x["size_mb"], reverse=True)[:20], + "entwine_available": ENTWINE_AVAILABLE, + "entwine_path": ENTWINE_PATH, + } + + +@router.delete("/delete/{pc_id}") +def delete_pointcloud(pc_id: str): + out_dir = EPT_DIR / pc_id + if not out_dir.exists(): + raise HTTPException(status_code=404, detail=f"ID {pc_id} non trouvé") + + try: + for ext in [".las", ".laz", ".ply", ".xyz", ".pts"]: + original = UPLOADS_DIR / f"{pc_id}{ext}" + if original.exists(): + original.unlink() + + shutil.rmtree(out_dir) + return {"ok": True, "message": f"Nuage {pc_id} supprimé"} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erreur suppression : {str(e)}") \ No newline at end of file diff --git a/backend/routes/upload.py b/backend/routes/upload.py new file mode 100644 index 0000000..cf45471 --- /dev/null +++ b/backend/routes/upload.py @@ -0,0 +1,47 @@ +from fastapi import APIRouter, UploadFile, File, HTTPException +from fastapi.responses import JSONResponse +import uuid, shutil, time +from pathlib import Path +from config import UPLOADS_DIR, EPT_DIR, SUPPORTED_FORMATS +from services.converter import run_entwine, ENTWINE_AVAILABLE + +router = APIRouter() + +@router.post("/upload") +async def upload(file: UploadFile = File(...)): + suffix = Path(file.filename).suffix.lower() + if suffix not in SUPPORTED_FORMATS: + raise HTTPException(400, f"Format non supporté: {suffix}") + if not ENTWINE_AVAILABLE: + raise HTTPException(500, "entwine non disponible sur ce serveur") + + pc_id = str(uuid.uuid4())[:8] + upload_path = UPLOADS_DIR / f"{pc_id}{suffix}" + out_dir = EPT_DIR / pc_id + + file_size = 0 + with open(upload_path, "wb") as f: + while chunk := await file.read(1024 * 1024): + f.write(chunk) + file_size += len(chunk) + + if file_size == 0: + upload_path.unlink() + raise HTTPException(400, "Fichier vide") + + if out_dir.exists(): + shutil.rmtree(out_dir, ignore_errors=True) + out_dir.mkdir(parents=True, exist_ok=True) + + start = time.time() + result = run_entwine(upload_path, out_dir) + + return JSONResponse({ + "id": pc_id, + "filename": file.filename, + "size_mb": round(file_size / (1024 * 1024), 2), + "viewer_path": f"/viewer/{pc_id}", + "embed_path": f"/viewer-embed/{pc_id}", + "ept_dir": result.get("ept_dir"), + "conversion_time_seconds": round(time.time() - start, 2), + }) \ No newline at end of file diff --git a/backend/routes/viewer.py b/backend/routes/viewer.py new file mode 100644 index 0000000..dfd5418 --- /dev/null +++ b/backend/routes/viewer.py @@ -0,0 +1,23 @@ +from fastapi import APIRouter, HTTPException +from fastapi.responses import HTMLResponse +from config import EPT_DIR +from services.manifest import read_manifest +from services.html_generator import generate_viewer_html + +router = APIRouter() + +@router.get("/viewer/{pc_id}") +def viewer(pc_id: str): + out_dir = EPT_DIR / pc_id + if not out_dir.exists(): + raise HTTPException(404, f"ID {pc_id} non trouvé") + manifest = read_manifest(out_dir) + return HTMLResponse(generate_viewer_html(pc_id, manifest.get("ept_dir"), embed=False)) + +@router.get("/viewer-embed/{pc_id}") +def viewer_embed(pc_id: str): + out_dir = EPT_DIR / pc_id + if not out_dir.exists(): + raise HTTPException(404, f"ID {pc_id} non trouvé") + manifest = read_manifest(out_dir) + return HTMLResponse(generate_viewer_html(pc_id, manifest.get("ept_dir"), embed=True)) \ No newline at end of file