From 048e09f7242afc98b9353cfbb9ee6f93e44c4e7a Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 3 Oct 2026 22:28:52 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BD=D0=B5=D1=81=D1=83=D1=89=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D0=B8=D0=B9=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=80=D1=88=D1=80=D1=83=D1=82=20=D0=BF=D0=BE=D0=B4=20/api/v1?= =?UTF-8?q?=20=E2=80=94=20404,=20=D0=B0=20=D0=BD=D0=B5=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=20SPA-=D0=B7=D0=B0=D0=B3=D0=BB=D1=83=D1=88?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При выключенном dev-эмитенте POST /api/v1/dev/token отвечал 405 от StaticFiles. Co-Authored-By: Claude Fable 5.1 --- api/src/app.py | 9 ++++++++- api/tests/test_me_and_dev_token.py | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api/src/app.py b/api/src/app.py index f3ff93e..370099a 100644 --- a/api/src/app.py +++ b/api/src/app.py @@ -27,7 +27,7 @@ from nats.aio.client import Client as NATS from starlette.middleware.gzip import GZipMiddleware from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware -from src.api.router import router +from src.api.router import API_PREFIX, router from src.core.clients import S3Storage from src.core.config import NATSSettings, get_settings from src.core.exception_handlers import register_exception_handlers @@ -211,8 +211,15 @@ def sep_static(path: str, stat_dir: str) -> str: return "/".join(sep_path[index:]) +API_STATIC_PATH = API_PREFIX.strip("/") + + class SPAStaticFiles(StaticFiles): async def get_response(self, path: str, scope): + # Несуществующий маршрут API — честный 404 (в т.ч. для POST), а не index.html SPA и не 405 от StaticFiles: + # иначе dev-маршруты, которых при выключенных флагах нет, выглядели бы как «существующие» + if path == API_STATIC_PATH or path.startswith(API_STATIC_PATH + "/"): + raise HTTPException(status_code=HTTPStatus.NOT_FOUND) try: return await super().get_response(path, scope) except (HTTPException, StarletteHTTPException) as ex: diff --git a/api/tests/test_me_and_dev_token.py b/api/tests/test_me_and_dev_token.py index e85c9b6..b773e8e 100644 --- a/api/tests/test_me_and_dev_token.py +++ b/api/tests/test_me_and_dev_token.py @@ -362,3 +362,12 @@ def test_module_router_matches_settings() -> None: """Модульный `router` собран по настройкам процесса (`.env.test`: DEBUG и AUTH_DEV_ISSUER включены).""" paths = {getattr(r, "path", None) for r in router.routes} assert {"/api/v1/me", "/api/v1/dev/token"} <= paths + + +@pytest.mark.parametrize("method", ["GET", "POST"]) +async def test_unknown_api_path_is_404_not_spa(method: str) -> None: + """Под `/api/v1` SPA-заглушка не отвечает: выключенный dev-маршрут — 404 (не 405, не index.html).""" + app_module = importlib.import_module("src.app") + async with _client(app_module.app) as client: + response = await client.request(method, "/api/v1/dev/no-such-route", json={}) + assert response.status_code == HTTP_NOT_FOUND