fix: несуществующий маршрут под /api/v1 — 404, а не ответ SPA-заглушки

При выключенном dev-эмитенте POST /api/v1/dev/token отвечал 405 от StaticFiles.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-03 22:28:52 +04:00
parent f5a5bbf95f
commit 048e09f724
2 changed files with 17 additions and 1 deletions

View File

@ -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:

View File

@ -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