From 8da7c48bb877887445d1c598a21bd6f64b04e761 Mon Sep 17 00:00:00 2001 From: Hermes Team Date: Sun, 23 Aug 2026 20:40:38 +0700 Subject: [PATCH] =?UTF-8?q?fix(web):=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=20=D0=BE=D1=82=D0=B4=D0=B0=D1=91=D1=82=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81,=20=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A15 и A16 сошлись в пустоту: API отвечал, файлы клиента лежали в репозитории, но server.py не монтировал static — в браузере был 404 и до интерфейса было не добраться. Причина организационная и она на ревьюере: контракт описал каталог static/ в структуре пакета, но в разделе об эндпоинтах не назвал, кто его отдаёт. Обе стороны выполнили написанное и всё равно не собрались. Подключены StaticFiles и корневой маршрут. Проверено исполнением: / -> 200 (13.5 КБ), /app.js -> 200 (51 КБ), /style.css -> 200 (21 КБ), /api/health -> 200, /api/snapshot -> 200 (98 КБ). Тесты: 325 passed, ruff чисто. Co-Authored-By: Claude Opus 5 --- src/antigravity_provider/router/web/server.py | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/antigravity_provider/router/web/server.py b/src/antigravity_provider/router/web/server.py index 8254602..1d781c0 100644 --- a/src/antigravity_provider/router/web/server.py +++ b/src/antigravity_provider/router/web/server.py @@ -9,7 +9,10 @@ from typing import Any, Dict from antigravity_provider import paths from antigravity_provider.version import __version__ from fastapi import FastAPI, Request, HTTPException, Depends, Header -from fastapi.responses import JSONResponse +from pathlib import Path + +from fastapi.responses import FileResponse, JSONResponse +from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from fastapi.encoders import jsonable_encoder @@ -131,3 +134,32 @@ def run_server(): logger.info(f"Starting Hermes Hub Web API on {host}:{port}") uvicorn.run(app, host=host, port=port, log_level="warning") + + +# ───────────────────────────────────────────────────────────── +# Статика: сервер обязан отдавать сам интерфейс, а не только API. +# Контракт описывал каталог static/, но не назвал, кто его монтирует, +# поэтому серверная и клиентская стороны сошлись в пустоту: API +# отвечал, файлы лежали в репозитории, а в браузере был 404. +# ───────────────────────────────────────────────────────────── + +_STATIC_DIR = Path(__file__).resolve().parent / "static" + +if _STATIC_DIR.is_dir(): + app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static") + + @app.get("/") + def index(): + return FileResponse(str(_STATIC_DIR / "index.html")) + + @app.get("/app.js") + def _app_js(): + return FileResponse(str(_STATIC_DIR / "app.js"), media_type="application/javascript") + + @app.get("/style.css") + def _style_css(): + return FileResponse(str(_STATIC_DIR / "style.css"), media_type="text/css") + + @app.get("/snapshot.example.json") + def _fixture(): + return FileResponse(str(_STATIC_DIR / "snapshot.example.json"), media_type="application/json")