GitLab Repo

amachine.am_app

1from .am_server import create_app
2 
3__all__ = ["create_app"]
4 
def create_app(experiments_root: pathlib.Path) -> fastapi.applications.FastAPI:
19def create_app(experiments_root: Path) -> FastAPI:
20
21    @asynccontextmanager
22    async def lifespan(app: FastAPI):
23        app.state.experiments_root = experiments_root
24        yield
25
26    app = FastAPI(title="amachine", lifespan=lifespan)
27
28    # -------------------------------------------------------------------------
29    # Development Middleware: Prevent JS Caching
30    # -------------------------------------------------------------------------
31
32    @app.middleware("http")
33    async def prevent_js_caching(request: Request, call_next):
34        response = await call_next(request)
35        if request.url.path.endswith(".js"):
36            response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
37            response.headers["Pragma"] = "no-cache"
38            response.headers["Expires"] = "0"
39        return response
40
41    app.add_middleware(
42        CORSMiddleware,
43        allow_origins=["*"],
44        allow_methods=["*"],
45        allow_headers=["*"],
46    )
47
48    # -------------------------------------------------------------------------
49    # WebSocket
50    # -------------------------------------------------------------------------
51
52    @app.websocket("/ws")
53    async def websocket_endpoint(websocket: WebSocket):
54        await websocket.accept()
55        try:
56            while True:
57                raw = await websocket.receive_text()
58                msg = orjson.loads(raw)
59                await _handle_message(websocket, msg, app.state.experiments_root)
60        except WebSocketDisconnect:
61            pass
62
63    # -------------------------------------------------------------------------
64    # REST (convenience / direct access)
65    # -------------------------------------------------------------------------
66
67    @app.get("/api/experiments", response_model=list[ExperimentSummary])
68    def list_experiments():
69        return scan_experiments(app.state.experiments_root)
70
71    @app.get("/api/experiments/{experiment_id}", response_model=ExperimentDetail)
72    def experiment_detail(experiment_id: str):
73        try:
74            return get_experiment( app.state.experiments_root, experiment_id )
75        except FileNotFoundError as e:
76            raise HTTPException(status_code=404, detail=str(e))
77
78    # -------------------------------------------------------------------------
79    # Static UI
80    # -------------------------------------------------------------------------
81
82    if _UI_DIR.exists():
83        app.mount("/", StaticFiles(directory=_UI_DIR, html=True), name="ui")
84
85    return app