diff --git a/inferedgelab/studio/routes.py b/inferedgelab/studio/routes.py index 5d69642..9360a4e 100644 --- a/inferedgelab/studio/routes.py +++ b/inferedgelab/studio/routes.py @@ -12,6 +12,7 @@ from fastapi import Body from fastapi.responses import FileResponse from fastapi.responses import RedirectResponse +from fastapi.responses import Response from inferedgelab.compare.comparator import compare_results from inferedgelab.compare.judgement import judge_comparison @@ -61,6 +62,26 @@ def studio_korean_particle_redirect() -> RedirectResponse: @router.get("/studio/static/{asset_name}", include_in_schema=False) def studio_static(asset_name: str) -> FileResponse: + return _studio_static_response(asset_name) + + +@router.get("/app.js", include_in_schema=False) +def studio_file_preview_app() -> Response: + return _studio_file_preview_placeholder( + "application/javascript", + "// Local server placeholder: /studio/static/app.js owns Studio initialization.\n", + ) + + +@router.get("/style.css", include_in_schema=False) +def studio_file_preview_style() -> Response: + return _studio_file_preview_placeholder( + "text/css", + "/* Local server placeholder: /studio/static/style.css owns Studio styling. */\n", + ) + + +def _studio_static_response(asset_name: str) -> FileResponse: media_type = STATIC_ASSETS.get(asset_name) if media_type is None: raise HTTPException(status_code=404, detail="studio asset not found") @@ -71,6 +92,14 @@ def studio_static(asset_name: str) -> FileResponse: ) +def _studio_file_preview_placeholder(media_type: str, content: str) -> Response: + return Response( + content=content, + media_type=media_type, + headers={"Cache-Control": "no-store"}, + ) + + @router.get("/studio/api/jobs", include_in_schema=False) def studio_jobs(request: Request) -> dict[str, Any]: store = _get_studio_job_store(request) diff --git a/tests/test_studio_routes.py b/tests/test_studio_routes.py index f21763c..a08335d 100644 --- a/tests/test_studio_routes.py +++ b/tests/test_studio_routes.py @@ -5,6 +5,7 @@ from fastapi.responses import FileResponse from fastapi.responses import RedirectResponse +from fastapi.responses import Response import inferedgelab.api as api @@ -100,6 +101,26 @@ def test_studio_static_assets_are_served(): assert "pipeline-flow" in Path(style_response.path).read_text(encoding="utf-8") +def test_studio_file_preview_asset_fallbacks_are_served(): + app = api.create_app() + app_route = _get_route(app, "/app.js") + style_route = _get_route(app, "/style.css") + + app_response = app_route.endpoint() + style_response = style_route.endpoint() + + assert isinstance(app_response, Response) + assert isinstance(style_response, Response) + assert app_response.status_code == 200 + assert style_response.status_code == 200 + assert app_response.headers["cache-control"] == "no-store" + assert style_response.headers["cache-control"] == "no-store" + assert app_response.media_type == "application/javascript" + assert style_response.media_type == "text/css" + assert b"/studio/static/app.js owns Studio initialization" in app_response.body + assert b"/studio/static/style.css owns Studio styling" in style_response.body + + def test_studio_static_assets_include_redesigned_ui_contracts(): app = api.create_app() route = _get_route(app, "/studio/static/{asset_name}")