Files
PlantAI-dev/apps/web/af3_sandbox/main.py
T
2026-08-03 18:42:47 +08:00

81 lines
2.3 KiB
Python

from __future__ import annotations
import os
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
SANDBOX_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = SANDBOX_DIR.parents[2]
STATIC_DIR = SANDBOX_DIR / "static"
SHARED_STATIC_DIR = PROJECT_ROOT / "apps" / "web" / "vue3" / "static"
DATA_ROOT = Path(
os.environ.get(
"AF3_SANDBOX_DATA_ROOT",
"/home/xzq/5090/00test/af3/af3_outputs/gcn4_dimer_demo",
)
).resolve()
ALLOWED_SUFFIXES = {".cif", ".mmcif"}
MOLSTAR_VERSION = (
STATIC_DIR / "vendor" / "molstar" / "VERSION"
).read_text(encoding="utf-8").strip()
app = FastAPI(title="AF3 Molstar Sandbox")
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
app.mount(
"/shared-static",
StaticFiles(directory=SHARED_STATIC_DIR),
name="shared-static",
)
def resolve_structure(filename: str) -> Path:
requested = Path(filename)
if requested.name != filename or requested.suffix.lower() not in ALLOWED_SUFFIXES:
raise HTTPException(status_code=404, detail="structure not found")
candidate = (DATA_ROOT / filename).resolve()
if candidate.parent != DATA_ROOT or not candidate.is_file():
raise HTTPException(status_code=404, detail="structure not found")
return candidate
@app.get("/")
def index() -> FileResponse:
return FileResponse(SANDBOX_DIR / "index.html")
@app.get("/api/health")
def health() -> dict[str, object]:
return {
"status": "ok",
"data_root": str(DATA_ROOT),
"data_root_available": DATA_ROOT.is_dir(),
"molstar_version": MOLSTAR_VERSION,
}
@app.get("/api/structures")
def list_structures() -> dict[str, list[dict[str, object]]]:
if not DATA_ROOT.is_dir():
return {"structures": []}
structures = [
{"name": path.name, "size": path.stat().st_size}
for path in DATA_ROOT.iterdir()
if path.is_file() and path.suffix.lower() in ALLOWED_SUFFIXES
]
structures.sort(key=lambda item: str(item["name"]))
return {"structures": structures}
@app.get("/api/structures/{filename}")
def get_structure(filename: str) -> FileResponse:
path = resolve_structure(filename)
return FileResponse(
path,
media_type="chemical/x-mmcif",
filename=path.name,
)