File size: 2,096 Bytes
0e24aff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"""Space entrypoint: physix.server.app:app + static UI mount.

Imported at runtime by the Dockerfile's CMD via ``uvicorn _space_app:app``.

What this wrapper adds on top of ``physix.server.app:app``:

  1. ``GET /``            -> 302 to ``/web/`` (so the bare Space URL
                             doesn't 404 — OpenEnv's ``create_fastapi_app``
                             does NOT add a root redirect; that's only in
                             the higher-level wrapper which mounts Gradio
                             at ``/web`` and would clobber our React SPA).
  2. ``GET /web``         -> 302 to ``/web/`` (same reason; users hit the
                             no-trailing-slash variant from outside links).
  3. ``StaticFiles`` mount at ``/web/`` serving the built Vite SPA. The
     vite build was run with ``--base=/web/`` so all asset URLs in the
     emitted ``index.html`` already include the prefix.

Kept as a real .py file (not a heredoc inside the Dockerfile) so any
syntax error is caught by the build's static analysis rather than at
runtime — saved several deploy-fail loops in earlier iterations.
"""

from __future__ import annotations

from pathlib import Path

from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles

from physix.server.app import app

_STATIC_DIR = Path("/app/static")


@app.get("/", include_in_schema=False)
async def _root_redirect() -> RedirectResponse:
    return RedirectResponse(url="/web/")


@app.get("/web", include_in_schema=False)
async def _web_no_slash_redirect() -> RedirectResponse:
    return RedirectResponse(url="/web/")


if _STATIC_DIR.is_dir():
    # html=True makes StaticFiles serve index.html for directory hits and
    # fall back to it for unknown sub-paths (so client-side React routing
    # works). Mounted last so registered API routes (/web/metadata,
    # /web/reset, /web/step from OpenEnv; /interactive/* from physix)
    # always win over the static handler.
    app.mount(
        "/web",
        StaticFiles(directory=str(_STATIC_DIR), html=True),
        name="ui",
    )