Doc Gateway - PlutoGr/TravelAgency GitHub Wiki

Gateway

TravelAgency.Gateway (src/Gateway/) это единая точка входа в API. Он не хранит данных и не содержит бизнес-логики: принимает запрос, проверяет вход и роль, ограничивает частоту и отправляет запрос в нужный сервис через YARP.

Простой пример

Браузер:  POST /api/v1/bookings   Cookie: access_token=eyJ…
Gateway:  cookie → Authorization: Bearer eyJ…
          маршрут bookings-route, политика RequireAuthenticated: роль Client, проходит
          убрать /api/v1 → POST http://booking-service:8080/bookings
Booking:  201 Created { "id": "…", "status": 0, … }

Без cookie тот же запрос получит 401 прямо на Gateway и до Booking не дойдёт.

Порядок обработки запроса

Порядок задан в Program.cs и важен: каждый шаг опирается на предыдущие.

№ Шаг Что делает
1 Forwarded headers берёт реальный IP и схему из X-Forwarded-For, X-Forwarded-Proto, но только от адресов частных сетей (nginx в Docker)
2 Заголовки безопасности X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer, Content-Security-Policy: default-src 'self' и др.
3 Логирование запроса Serilog
4 CorrelationIdMiddleware читает или создаёт X-Correlation-Id, кладёт его в логи и в ответ
5 GlobalExceptionHandlerMiddleware любое необработанное исключение превращается в 500 ProblemDetails
6 CORS политика GatewayPolicy
7 Лимиты запросов 429 при превышении
8 CookieToAuthHeaderMiddleware cookie access_token → заголовок Authorization: Bearer; для хаба чата также параметр access_token в строке запроса
9 Authentication, Authorization проверка JWT и политики маршрута
10 AuthRequestMiddleware для пустого POST /auth/refresh и /auth/logout подставляет тело { refreshToken } из cookie
11 Health checks /health/live, /health/ready
12 Заглушка presign и YARP /api/v1/media/presign отвечает 404; остальное проксируется

Ответы маршрута auth дополнительно проходят через AuthResponseTransformProvider. На login, register и refresh токены из ответа Identity переносятся в cookie, клиент получает { "success": true }. Gateway проверяет ответ Identity: если в ответе 200 или 201 на этих путях нет обоих токенов и сроков в будущем, или токен пришёл в 2xx на другом пути маршрута auth, клиент получает 502 без Set-Cookie, и тело Identity в JS не уходит. Подробно: Вход и сессия.

Маршруты

Полная таблица маршрутов, методов и прав: Публичный API. Коротко:

  • 11 маршрутов YARP в appsettings.json (секция ReverseProxy:Routes). Политика доступа задаётся полем AuthorizationPolicy у каждого маршрута.
  • При пересечении путей решает Order: catalog-wizard-route (-1) раньше публичного GET каталога, поэтому гость на /catalog/manage/** получает 401; chat-hub-route (0) раньше chat-route (1); маршруты media/files и media/manage (0) раньше общего media-route (100).
  • Преобразование пути: у большинства маршрутов убирается /api/v1; у auth-route убирается /api/v1/auth и добавляется /identity; у chat-hub-route убирается /api/v1/chat.
  • Кластеры: identity-cluster, catalog-cluster, booking-cluster, chat-cluster, media-cluster. В Docker адреса http://<имя>-service:8080, в Development http://localhost:5010 ... 5050.

Политики доступа

Политика Роли
Anonymous без входа
RequireAuthenticated Client, Manager, Admin
RequireManager Manager, Admin
RequireManagerOrAdmin Manager, Admin
RequireAdmin Admin

Имена политик и ролей общие для всех сервисов (TravelAgency.Shared.Contracts). Gateway проверяет только роль; права на конкретный объект (своя заявка, свой тур) проверяет сервис.

JWT

Проверяются подпись (ключ JwtSettings__SigningKey, не короче 32 символов), Issuer = TravelAgency.Identity, Audience = TravelAgency и срок жизни. Без ключа Gateway не запускается.

Лимиты запросов

Встроенный RateLimiter ASP.NET Core с глобальным разбиением по IP клиента:

Группа Лимит Окно
login, register, refresh, logout 5 запросов (RateLimiting:Auth:Limit) 1 секунда
всё остальное 200 запросов (RateLimiting:Global:Limit) 1 минута

Если задана строка ConnectionStrings__Redis (на dev задана), счётчики хранятся в Redis (RedisFixedWindowRateLimiter) и общие для всех экземпляров Gateway. Если Redis недоступен и RateLimiting:FailOpen = true (по умолчанию), запросы пропускаются. Без Redis счётчики в памяти процесса.

Лимит на вход задаётся только RateLimiting:Auth:Limit (по умолчанию 5) и RateLimiting:Auth:PeriodSeconds (по умолчанию 1). В appsettings.json явно заданы 5 запросов и окно в 1 секунду, поэтому так же работает dev и локальный Docker. Шестой POST /api/v1/auth/login в этом окне получает 429.

Фронтенд при 429 показывает под формой входа «Слишком много попыток входа. Подождите немного и попробуйте снова.». Введённая почта остаётся, пароль очищается, кнопка «Войти» снова доступна, следующая попытка уходит обычным запросом. Неверный пароль и недоступность сервиса показываются как раньше. Сужение доверенных прокси для X-Forwarded-For (сейчас доверены все частные сети): задача #32.

CORS

Разрешённые origin задаются Cors:AllowedOrigins. На сервере это https://<SITE_DOMAIN> (через Cors__AllowedOrigins__0 в compose), в Development http://localhost:3000 и http://localhost:5173. Значение по умолчанию в appsettings.json заглушка и на dev переопределяется. Разрешены методы GET, POST, PUT, PATCH, DELETE, OPTIONS, заголовки Authorization, Content-Type, Accept, X-Correlation-Id, X-Request-ID, Cache-Control, и передача cookie.

Здоровье

  • GET /health/live: процесс жив, зависимости не проверяются.
  • GET /health/ready: Gateway запрашивает /health/live каждого сервиса (таймаут 5 секунд). Ответ Healthy, если все ответили 2xx, иначе Degraded. Снаружи этот ответ виден как /healthz на nginx.

Настройки

Секция Что задаёт
ReverseProxy маршруты и кластеры YARP
AuthRoute пути login, register, refresh, logout для cookie-преобразований
Cookie имена cookie, Secure, SameSite=Lax, Path=/, необязательный Domain
JwtSettings Issuer, Audience, ключ подписи (ключ только из окружения)
RateLimiting лимиты и FailOpen
Cors разрешённые origin, методы, заголовки
HealthCheckEndpoints адреса /health/live сервисов
Otel адрес OTLP-экспорта трассировки
ConnectionStrings:Redis Redis для лимитов (необязательно)

Наблюдаемость

Serilog с {CorrelationId} в шаблоне строки, OpenTelemetry (инструментация ASP.NET Core и HttpClient), сервис регистрируется как TravelAgency.Gateway.

См. также: Архитектура · Публичный API

⚠️ **GitHub.com Fallback** ⚠️