Реализована интеграция с СМС провайдером
This commit is contained in:
@@ -19,7 +19,7 @@ HAN Chat - приложение для мигрантов, где стартов
|
||||
- Мультиязычность в первом релизе не нужна, но тексты должны храниться по мнемоникам для будущих переводов.
|
||||
- Среда на первом этапе одна и проектируется как боевая.
|
||||
- Вложения чата MVP: **только изображения и PDF** — см. [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), «Разрешённые типы файлов чата».
|
||||
- SMS OTP на старте: **заглушка** — пользователь вводит фиксированный код из `.env` (`KEYCLOAK_OTP_MOCK_CODE`); SMS не отправляется. Интеграция с SMS-провайдерами — в бэклоге (см. [`!Backlog.md`](../../HAN_chat/!Backlog.md)).
|
||||
- SMS OTP вводится поэтапно: до production rollout действует явный mock (`KEYCLOAK_OTP_MOCK_ENABLED=true`); целевой real mode — Keycloak генерирует/локально проверяет OTP и создаёт durable order в `sms-service`, а worker асинхронно вызывает i-Digital Direct. Контракт и gates — [`module-11-idgtl-sms.md`](../modules/module-11-idgtl-sms.md).
|
||||
- Популярный вопрос при выборе **автоматически отправляется как сообщение**; если пользователь не авторизован — сначала согласия и OTP, затем отправка.
|
||||
- Перечень таблиц и миграций App DB проектирует модуль `database` (и владельцы схем других сервисов); arch фиксирует только **разделение схем** PostgreSQL и контракты между сервисами.
|
||||
|
||||
@@ -42,12 +42,13 @@ HAN Chat - приложение для мигрантов, где стартов
|
||||
|
||||
- Expo App: единая frontend-кодовая база для iOS, Android и web.
|
||||
- Keycloak: identity provider, OTP-only авторизация по номеру телефона.
|
||||
- SMS Service: internal durable order API, шаблоны и бессрочный журнал SMS; отдельный worker вызывает i-Digital Direct, callback обновляет только журнал.
|
||||
- api-backend: Python-приложение с REST API, realtime-доставкой сообщений и бизнес-логикой.
|
||||
- Nginx Reverse Proxy: единая публичная точка входа, HTTPS termination и маршрутизация на Keycloak/API/frontend web/Bitrix24.
|
||||
- Message Safety Service: отдельный сервис проверки входящих сообщений; вызов из API → `200 allow` | `403 deny` | `203 pending` + `task_id` (при `203` api-backend синхронно поллит task до финального вердикта, без очереди анализа на api-backend).
|
||||
- Bitrix24 Local App: локальное приложение, custom connector `han_mobile_app` для Bitrix24 Open Lines: чат, OAuth, webhook-события, маппинг `dialog_id` ↔ `bitrix_chat_id`.
|
||||
- Bitrix24 sync service: двусторонняя синхронизация App DB ↔ Битрикс24 CRM (Contact на MVP; маппинг ID, очередь через триггеры, webhook от роботов Bitrix24).
|
||||
- Managed PostgreSQL (приватная сеть, одна база): схемы `han_app`, `bitrix_sync`, `bitrix_local`, `keycloak`, `message_safety` — отдельный DB-user на схему.
|
||||
- Managed PostgreSQL (приватная сеть, одна база): схемы `han_app`, `bitrix_sync`, `bitrix_local`, `keycloak`, `message_safety`, `sms` — отдельный DB-user на схему.
|
||||
- Redis: rate limits API и realtime/service coordination (**не** OTP counters — они в Keycloak/SPI);
|
||||
- S3-data: production-хранилище проверенных файлов чата (`han-chat-attachments`) и документов компании (`han-chat-documents`).
|
||||
- S3-quarantine: временное хранилище загруженных файлов до вердикта Message Safety Service (`han-chat-quarantine`); read-only для `message-safety`.
|
||||
@@ -57,7 +58,7 @@ HAN Chat - приложение для мигрантов, где стартов
|
||||
|
||||
На первом этапе весь backend-контур работает на **одной VM** в облаке провайдера:
|
||||
|
||||
- `nginx`, `api-backend`, `message-safety`, `keycloak`, `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector` — в Docker Compose на VM;
|
||||
- `nginx`, `api-backend`, `message-safety`, `keycloak`, `sms-service`/worker, `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector` — в Docker Compose на VM;
|
||||
- публичный доступ из интернета только через `nginx` (порты 80/443);
|
||||
- внутренние сервисы общаются по Docker-сети на localhost VM.
|
||||
|
||||
@@ -72,6 +73,7 @@ HAN Chat - приложение для мигрантов, где стартов
|
||||
| одна база / `message_safety` | `message-safety` | verdict cache, safety_task, rule config |
|
||||
| одна база / `bitrix_local` | `bitrix-local-app` | OAuth, inbox, `dialog_sessions` |
|
||||
| одна база / `keycloak` | Keycloak | учётные записи, realm, сессии IdP |
|
||||
| одна база / `sms` | `sms-service`, `sms-worker` | шаблоны, runtime settings, бессрочный журнал отправки/доставки SMS |
|
||||
|
||||
Redis на первом этапе остаётся на VM в Docker (ephemeral/coordination). Selectel S3 — внешнее object storage: три бакета (`han-chat-quarantine`, `han-chat-attachments`, `han-chat-documents`); см. [`arch-00-glossary.md`](arch-00-glossary.md).
|
||||
|
||||
@@ -82,6 +84,9 @@ flowchart LR
|
||||
Client[Expo Mobile/Web App]
|
||||
Nginx[Nginx Reverse Proxy]
|
||||
Keycloak[Keycloak OTP]
|
||||
SMS[SMS Service]
|
||||
SMSWorker[SMS Worker]
|
||||
Direct[i-Digital Direct]
|
||||
API[Python api-backend]
|
||||
Safety[Message Safety Service]
|
||||
DB[(PostgreSQL)]
|
||||
@@ -95,8 +100,14 @@ flowchart LR
|
||||
|
||||
Client -->|HTTPS REST + Realtime| Nginx
|
||||
Nginx -->|/auth| Keycloak
|
||||
Nginx -->|exact POST /callbacks/idgtl/sms| SMS
|
||||
Nginx -->|"/api REST + WS realtime"| API
|
||||
Keycloak --> DB
|
||||
Keycloak -->|durable SMS order| SMS
|
||||
SMS --> DB
|
||||
SMSWorker --> DB
|
||||
SMSWorker -->|HTTPS POST /api/v1/message| Direct
|
||||
Direct -->|delivery callback| Nginx
|
||||
API --> DB
|
||||
API --> Redis
|
||||
Client -->|presigned PUT| S3Q
|
||||
@@ -222,8 +233,9 @@ Frontend не должен:
|
||||
Отвечает за:
|
||||
|
||||
- OTP-only регистрацию и вход;
|
||||
- OTP по номеру телефона; проверка кода — в Keycloak (заглушка `KEYCLOAK_OTP_MOCK_*` или SMS-провайдер, см. arch-04 и «Поток авторизации»);
|
||||
- продуктовые лимиты OTP (`otp.phone.*` из `app_settings`) через authenticator/SPI и settings bridge `api-backend` (см. arch-04); счётчики попыток — в зоне Keycloak (Redis DB Keycloak/SPI или in-memory Keycloak), **не** в `api-backend`;
|
||||
- OTP по номеру телефона; генерация и локальная проверка кода, challenge lifecycle, limits и verify audit — в Keycloak;
|
||||
- в real mode — заказ в `sms-service` по закрытому `POST /internal/sms/v1/send`; Keycloak ждёт только `200/202` + `sms_message_id`, не вызывает Direct и не читает provider statuses;
|
||||
- продуктовые лимиты OTP (`otp.phone.*` из `app_settings`) через authenticator/SPI и settings bridge `api-backend` (см. arch-04); durable counters/challenges/events — в provider-owned таблицах schema `keycloak`, **не** в Redis и не в `api-backend`;
|
||||
- хранение учетных записей;
|
||||
- выдачу и обновление токенов (access + refresh);
|
||||
- настройку realm, clients, roles, policies;
|
||||
@@ -238,7 +250,7 @@ Frontend не должен:
|
||||
| Expo frontend | Frontend → Keycloak (`/auth/*` через nginx) | OTP login (Authorization Code + PKCE), Refresh Token Grant, logout |
|
||||
| `api-backend` | api-backend → Keycloak JWKS/discovery | Валидация access token (issuer, audience, подпись); **не** вызывает Admin API в hot path |
|
||||
| Managed PostgreSQL | Keycloak → схема `keycloak` | Пользователи IdP, сессии, realm |
|
||||
| SMS-провайдер | Keycloak → SMS (post-MVP) | Доставка OTP; на MVP — mock code из `.env` |
|
||||
| `sms-service` | Keycloak → `sms-service` (real mode) | Durable order; service token, idempotency key и `sms_message_id` |
|
||||
|
||||
Confidential **backend client** Keycloak (client credentials) в MVP **не обязателен**: S2S между нашими сервисами идёт по service tokens, не через Keycloak. Client можно завести заранее в realm как optional для будущих admin/ops сценариев.
|
||||
|
||||
@@ -253,6 +265,7 @@ Confidential **backend client** Keycloak (client credentials) в MVP **не об
|
||||
- маршрутизацию `/auth/*` или выделенного auth-домена в Keycloak;
|
||||
- маршрутизацию публичных `/bitrix/*` endpoint в `bitrix-local-app`;
|
||||
- маршрутизацию `/bitrix/sync/*` webhook endpoint в `bitrix-sync`;
|
||||
- маршрутизацию только exact `POST /callbacks/idgtl/sms` в `sms-service` по HTTPS, с allowlist актуального IP Direct и без логирования Basic Authorization;
|
||||
- защиту internal endpoint `bitrix-local-app` через private network или `nginx allowlist`;
|
||||
- отсутствие публичной маршрутизации к `message-safety` — сервис доступен только из внутренней Docker-сети;
|
||||
- передачу `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, `X-Request-ID` (если клиент не прислал `X-Request-ID` — nginx **генерирует** UUID и прокидывает upstream);
|
||||
@@ -381,12 +394,12 @@ api-backend не решает, sync или async нужна проверка в
|
||||
5. Клиент может опционально согласиться на рекламные коммуникации.
|
||||
6. Если обязательные согласия не даны, отправка блокируется.
|
||||
7. Frontend запрашивает публичные настройки и показывает форму ввода номера телефона (единственный канал MVP).
|
||||
8. Keycloak запускает OTP-flow по телефону: клиент вводит номер, инициируется «отправка» OTP (при заглушке SMS фактически не уходит — см. arch-04).
|
||||
9. Лимиты OTP на **edge** — `nginx` (`NGINX_RATE_LIMIT_AUTH`); продуктовые лимиты `otp.phone.*` из `app_settings` применяются на стороне **Keycloak authenticator / SPI** (или обёртки OTP), не в `api-backend`. До интеграции SMS (mock OTP) достаточно edge + mock code.
|
||||
8. Keycloak запускает OTP-flow: в mock mode challenge сразу активен без SMS; в real mode Keycloak создаёт `ordering`, генерирует OTP, заказывает SMS в `sms-service` и активирует challenge только после durable order.
|
||||
9. Лимиты OTP на **edge** — `nginx`; продуктовые `otp.phone.*` применяет Keycloak. HTTP retry одного durable order использует прежние challenge/idempotency key и не увеличивает send counter.
|
||||
10. Клиент вводит OTP и отправляет его в Keycloak.
|
||||
11. **Keycloak проверяет корректность введённого OTP**:
|
||||
- при **`KEYCLOAK_OTP_MOCK_ENABLED=true`** (MVP и любой режим с включённой заглушкой): введённое значение должно **совпадать** с `KEYCLOAK_OTP_MOCK_CODE` из `.env`;
|
||||
- при **`KEYCLOAK_OTP_MOCK_ENABLED=false`** (после интеграции с SMS-провайдером, см. бэклог): введённое значение должно **совпадать** с одноразовым OTP, сгенерированным Keycloak и отправленным провайдером на телефон клиента (с учётом TTL и лимита попыток).
|
||||
- при **`KEYCLOAK_OTP_MOCK_ENABLED=false`**: значение сверяется локально с HMAC OTP, сгенерированного Keycloak и переданного в закрытом заказе `sms-service`; статусы Direct и callback на verify не влияют.
|
||||
- при неверном коде Keycloak возвращает ошибку; frontend не получает tokens, шаг 12 не выполняется.
|
||||
12. При успешной проверке frontend получает tokens через OIDC Authorization Code Flow with PKCE.
|
||||
13. Frontend с JWT вызывает **`POST /api/v1/auth/bootstrap`** — в теле передаёт локально принятые согласия и device metadata (см. arch-02). api-backend атомарно: `find-or-create` по JWT `sub` (`keycloak_sub`), телефон из JWT claims (не из body) → сохранение `UserConsent` на `user_id` → минимальный профиль.
|
||||
@@ -539,7 +552,7 @@ App DB — **локальный кэш** для UI. Двусторонний syn
|
||||
|
||||
### Состав backend-контура
|
||||
|
||||
Минимальный production-like контур на одной VM: `nginx`, `api-backend`, `message-safety`, `keycloak`, `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector`. Managed PostgreSQL и Selectel S3 находятся вне Docker Compose.
|
||||
Минимальный целевой real-SMS контур на одной VM: `nginx`, `api-backend`, `message-safety`, `keycloak`, `sms-service`/worker, `bitrix-sync`, `bitrix-local-app`, `redis`, `otel-collector`. До SMS rollout сервисы SMS могут отсутствовать, но Keycloak обязан оставаться в mock mode.
|
||||
|
||||
### Предлагаемая структура backend-репозитория
|
||||
|
||||
@@ -583,6 +596,11 @@ backend/
|
||||
realm/
|
||||
themes/
|
||||
providers/
|
||||
sms-service/
|
||||
app/
|
||||
migrations/
|
||||
openapi.yaml
|
||||
Dockerfile
|
||||
redis/
|
||||
docker-compose.yml
|
||||
observability/
|
||||
|
||||
Reference in New Issue
Block a user