API: закрыть схему в проде + security-заголовки на edge

main.py: openapi.json/docs/redoc отдаются только в dev/test (нужны для gen:api),
в production отключены. Caddyfile (edge): HSTS, X-Content-Type-Options, X-Frame-Options,
Referrer-Policy, Permissions-Policy, скрыт Server; CSP подготовлена, но выключена до
проверки на test-клоне (строгая политика ломает SPA/Telegram-виджет/SSE). #61

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
2026-09-13 17:19:42 +03:00
co-authored by Claude Opus 4.8
parent 0799aee684
commit ce7fbad58e
3 changed files with 49 additions and 4 deletions
+7 -3
View File
@@ -151,12 +151,16 @@ async def _lifespan(_app: FastAPI):
def create_app() -> FastAPI:
# Схему API (openapi.json + Swagger/ReDoc) отдаём только в dev/test: она нужна для
# `npm run gen:api` (генерация типов фронта) и удобной отладки. В production закрываем —
# незачем облегчать разведку поверхности API анонимам (#61).
docs_enabled = not settings.is_production
app = FastAPI(
title="Forbidden Stars API",
version="0.1.0",
openapi_url="/api/openapi.json",
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json" if docs_enabled else None,
docs_url="/api/docs" if docs_enabled else None,
redoc_url="/api/redoc" if docs_enabled else None,
lifespan=_lifespan,
)
+22
View File
@@ -0,0 +1,22 @@
"""Хардненинг API: раскрытие схемы закрыто в production, открыто в dev/test (#61, F6)."""
from __future__ import annotations
from fastapi.testclient import TestClient
from app.core import config
from app.main import create_app
def test_openapi_open_in_development(client: TestClient):
# Тесты идут в development (conftest) — схема доступна: нужна для `npm run gen:api`.
assert client.get("/api/openapi.json").status_code == 200
assert client.get("/api/docs").status_code == 200
def test_openapi_closed_in_production(monkeypatch):
monkeypatch.setattr(config.settings, "app_env", "production")
prod_app = create_app()
c = TestClient(prod_app)
assert c.get("/api/openapi.json").status_code == 404
assert c.get("/api/docs").status_code == 404
assert c.get("/api/redoc").status_code == 404