studio /studio: serve the rich home (gallery remove/edit/+ refs), not the minimal SPA
The web/dist chat SPA shadowed studio_home.html at /studio, dropping the per-image affordances: delx (remove → POST /v1/library/status deleted), the Fix dialog (type-and-edit → /v1/library/fix), and the + reference tray (→ /upload/image → /v1/library/compose, up to 5 refs). Serve the rich home explicitly; drop the dead /studio/assets route + the node web-build stage. web/login.html still ships (server.py serves /login). v0.17.4
This commit is contained in:
-12
@@ -1,12 +1,3 @@
|
||||
# ── studio-chat SPA (web/) ── the /v1/agent chat UI served at /studio. Built here
|
||||
# so the image is self-contained; the small Vite bundle lands at /app/web/dist.
|
||||
FROM node:20-slim AS web
|
||||
WORKDIR /web
|
||||
COPY web/package.json web/package-lock.json* ./
|
||||
RUN npm ci 2>/dev/null || npm install
|
||||
COPY web/ ./
|
||||
RUN npm run build
|
||||
|
||||
FROM python:3.11-slim AS base
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
@@ -34,9 +25,6 @@ RUN chmod +x /tmp/branding/apply-branding.sh && /tmp/branding/apply-branding.sh
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# The studio-chat SPA built above → served at /studio by middleware/studio_home.py.
|
||||
COPY --from=web /web/dist ./web/dist
|
||||
|
||||
# Vendor the Hanzo Studio custom-node packs (segmentation, controlnet, ipadapter,
|
||||
# upscaling, video, utils) at their pinned commits and install their deps with the
|
||||
# torch/numpy/transformers stack locked. See custom_nodes/hanzo-packs.txt.
|
||||
|
||||
@@ -51,11 +51,6 @@ log = logging.getLogger("studio.home")
|
||||
|
||||
_HTML = Path(__file__).with_name("studio_home.html")
|
||||
_ASSET_DIR = Path(__file__).parent
|
||||
# The studio-chat SPA (web/, a Vite bundle on @hanzo/ai talking to /v1/agent) built
|
||||
# into the image at /app/web/dist and served AT /studio. When the build is absent
|
||||
# (dev checkout, an image that skipped the web stage) /studio falls back to the
|
||||
# legacy studio_home.html so the route is never a dark page.
|
||||
_WEB_DIST = Path(__file__).resolve().parents[1] / "web" / "dist"
|
||||
_STATUSES = ("draft", "approved", "queued", "published", "deleted")
|
||||
_PROV_CACHE: dict[str, tuple[float, dict]] = {} # abspath -> (mtime, provenance)
|
||||
|
||||
@@ -742,31 +737,14 @@ def add_studio_home_routes(routes: web.RouteTableDef, server) -> None:
|
||||
|
||||
@routes.get("/studio")
|
||||
async def studio_home(request: web.Request):
|
||||
# no-store: the SPA HTML changes every release; without this, browsers
|
||||
# no-store: the home HTML changes every release; without this, browsers
|
||||
# (and any proxy) heuristically cache it and serve a STALE page after a
|
||||
# deploy — which is why UI fixes appeared "not working" to a user still
|
||||
# running yesterday's cached page. The page is tiny; always serve fresh.
|
||||
# Hashed assets keep their own immutable cache. Serve the studio-chat SPA
|
||||
# when built into the image; fall back to the legacy home otherwise.
|
||||
index = _WEB_DIST / "index.html"
|
||||
html = index.read_text() if index.is_file() else _HTML.read_text()
|
||||
return web.Response(text=html, content_type="text/html",
|
||||
# running yesterday's cached page. Always serve fresh; the page is small
|
||||
# and its shell.js/shell.css keep their own cache.
|
||||
return web.Response(text=_HTML.read_text(), content_type="text/html",
|
||||
headers={"Cache-Control": "no-store, must-revalidate"})
|
||||
|
||||
@routes.get("/studio/assets/{name}")
|
||||
async def studio_asset(request: web.Request):
|
||||
# The studio-chat SPA's hashed bundle (the hash IS the version → immutable).
|
||||
name = request.match_info["name"]
|
||||
assets = (_WEB_DIST / "assets").resolve()
|
||||
f = (assets / name).resolve()
|
||||
if f.parent != assets or not f.is_file():
|
||||
return web.Response(status=404)
|
||||
ctype = ("text/javascript" if name.endswith(".js")
|
||||
else "text/css" if name.endswith(".css")
|
||||
else "application/octet-stream")
|
||||
return web.Response(body=f.read_bytes(), content_type=ctype,
|
||||
headers={"Cache-Control": "public, max-age=31536000, immutable"})
|
||||
|
||||
@routes.get("/studio/shell.js")
|
||||
async def shell_js(request: web.Request):
|
||||
return _asset("shell.js", "text/javascript")
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "hanzo-studio"
|
||||
version = "0.17.3"
|
||||
version = "0.17.4"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user