Реализована интеграция с СМС провайдером

This commit is contained in:
mi
2026-07-23 11:49:15 +03:00
parent cc0163eb94
commit b1ed714d5b
89 changed files with 5934 additions and 202 deletions
+47 -7
View File
@@ -10,6 +10,7 @@
|---|---|---|
| **Инфраструктура** | `.env` | подключения, URL, секреты, nginx/TLS, service tokens |
| **Бизнес-логика** | таблица **`app_settings`** | лимиты, флаги, телефоны, типы файлов, CORS, consent URLs |
| **Настройки SMS runtime** | таблица **`sms.sms_setting`** | sender default, provider timeouts, callback flag, worker intervals |
| **Контент** | `text_resources`, `popular_questions` | тексты UI |
Managed PostgreSQL **поднимается до** развёртывания приложения. Бизнес-настройки **не дублируются** в `.env`: seed в `app_settings` выполняется миграцией/скриптом модуля `database` **до** первого запуска `api-backend`.
@@ -27,8 +28,8 @@ Managed PostgreSQL **поднимается до** развёртывания п
- секреты: S3, Bitrix OAuth, service tokens, webhook-тokens;
- параметры **nginx/TLS** и edge rate limits (`NGINX_RATE_LIMIT_*`);
- идентификация Keycloak: realm, audience, public/internal URL;
- **OTP-заглушка MVP** (`KEYCLOAK_OTP_MOCK_*`) — infra/dev-секрет, не бизнес-настройка;
- технические таймауты worker-ов (`MESSAGE_SAFETY_*`, интервалы `bitrix-sync`).
- переключатель и секрет временного OTP mock (`KEYCLOAK_OTP_MOCK_*`); mock обязателен до прохождения real-SMS rollout gates и запрещён как незаявленный fallback;
- технические параметры сервисов, пока профильная спецификация не определила service-owned settings; для `sms-service` runtime-параметры уже вынесены в `sms.sms_setting`.
**Запрещено в `.env` (→ только `app_settings`):**
@@ -85,7 +86,7 @@ Managed PostgreSQL **поднимается до** развёртывания п
| Группа | Ключи |
|---|---|
| Auth | `auth.phone.enabled`, `auth.password.enabled` |
| OTP (продукт; потребитель — Keycloak SPI через settings bridge api-backend) | `otp.phone.max_send_attempts_per_24h`, `otp.phone.min_seconds_between_attempts`, `otp.phone.max_verify_attempts` |
| OTP (продукт; потребитель — Keycloak SPI через settings bridge api-backend) | `otp.phone.max_send_attempts_per_24h`, `otp.phone.min_seconds_between_attempts`, `otp.phone.max_verify_attempts`, `otp.phone.code_length`, `otp.phone.ttl_seconds`, `otp.phone.sms_order_timeout_ms` |
| Оператор | `operator.call.phone` |
| Consent | `consent.personal_data.*`, `consent.user_agreement.*`, `consent.marketing.*` |
| Файлы чата | `chat.attachments.*` |
@@ -102,6 +103,9 @@ auth.password.enabled=false
otp.phone.max_send_attempts_per_24h=3
otp.phone.min_seconds_between_attempts=30
otp.phone.max_verify_attempts=5
otp.phone.code_length=6
otp.phone.ttl_seconds=60
otp.phone.sms_order_timeout_ms=3000
operator.call.phone=+74999591007
@@ -137,6 +141,27 @@ security.public_cache.max_age_seconds=3600
---
## Service-owned настройки `sms-service`
Параметры, изменение которых не меняет Compose, секреты, URL или сетевую топологию, хранятся в `sms.sms_setting`, а не в `.env`.
Ключи и seed:
```text
provider.idgtl.default_sender_name=<approved>
provider.idgtl.connect_timeout_ms=3000
provider.idgtl.request_timeout_ms=70000
provider.idgtl.callback_enabled=true
worker.poll_interval_ms=500
worker.lease_seconds=90
```
В `.env` остаются только `SMS_DATABASE_URL`, URL внутренних/внешних сервисов, service tokens, Direct API key и callback credentials. Детальный контракт — `module-11-idgtl-sms.md`.
`<approved>` — обязательный deployment placeholder, а не допустимое production-значение. Перед real mode должны существовать active approved template `auth_otp` с точными placeholders `code`/`ttl_min` и согласованный `senderName`. Отсутствие template/sender делает readiness false.
---
## Пример `.env.example`
Только инфраструктура. Бизнес-параметры — в seed `app_settings`.
@@ -161,6 +186,7 @@ BITRIX_DATABASE_URL=postgresql://bitrix_local_app:change-me@<HAN_PG_HOST>:<HAN_P
BITRIX_SYNC_APP_DATABASE_URL=postgresql://bitrix_sync_user:change-me@<HAN_PG_HOST>:<HAN_PG_PORT>/<HAN_PG_DATABASE>
BITRIX_SYNC_DATABASE_URL=postgresql://bitrix_sync_user:change-me@<HAN_PG_HOST>:<HAN_PG_PORT>/<HAN_PG_DATABASE>
MESSAGE_SAFETY_DATABASE_URL=postgresql://message_safety_app:change-me@<HAN_PG_HOST>:<HAN_PG_PORT>/<HAN_PG_DATABASE>
SMS_DATABASE_URL=postgresql://sms_user:change-me@<HAN_PG_HOST>:<HAN_PG_PORT>/<HAN_PG_DATABASE>
KEYCLOAK_DB_URL=jdbc:postgresql://<HAN_PG_HOST>:<HAN_PG_PORT>/<HAN_PG_DATABASE>?user=keycloak_user&password=change-me&currentSchema=keycloak
KC_DB_URL_PROPERTIES=currentSchema=keycloak
# Selectel PgBouncer 5433: pool_mode=session; search_path задаётся на уровне ролей.
@@ -190,7 +216,7 @@ NGINX_RATE_LIMIT_PUBLIC=60r/m
NGINX_RATE_LIMIT_POLLING=60r/m
# =============================================================================
# Keycloak (infra; OTP-заглушка — dev/MVP)
# Keycloak (mock остаётся true до controlled SMS cutover)
# =============================================================================
KEYCLOAK_PUBLIC_URL=https://tohin.ru/auth
KEYCLOAK_INTERNAL_URL=http://keycloak:8080
@@ -198,6 +224,7 @@ KEYCLOAK_REALM=han-chat
KEYCLOAK_AUDIENCE=han-chat-api
KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_OTP_MOCK_CODE=1234
KEYCLOAK_SMS_SERVICE_URL=http://sms-service:8080
# =============================================================================
# Redis (I4: раздельные DB index)
@@ -219,6 +246,17 @@ BITRIX_INTERNAL_API_TOKEN=change-me
BITRIX_API_FORWARD_TOKEN=change-me
BITRIX_SYNC_SERVICE_TOKEN=change-me
KEYCLOAK_SETTINGS_BRIDGE_TOKEN=change-me
SMS_SERVICE_TOKEN=change-me
KEYCLOAK_SMS_SERVICE_TOKEN=change-me
# =============================================================================
# SMS provider (URL и секреты; runtime-параметры — sms.sms_setting)
# =============================================================================
IDGTL_SMS_BASE_URL=https://direct.i-dgtl.ru
IDGTL_SMS_API_KEY=change-me
IDGTL_SMS_CALLBACK_PUBLIC_URL=https://tohin.ru/callbacks/idgtl/sms
IDGTL_SMS_CALLBACK_USERNAME=change-me
IDGTL_SMS_CALLBACK_PASSWORD=change-me
# =============================================================================
# api-backend (интеграции + resilience I2)
@@ -297,6 +335,8 @@ presigned URL и CORS Selectel; path-style адресация не поддер
Все переменные — **только** в `backend/.env`. Отдельного хранилища нет.
Для production `change-me`, `<...>`, примерные sender/template/API key/callback credentials отклоняются `validate-env`. `IDGTL_SMS_API_KEY` — выданный Direct готовый `TOKEN_1` для Basic, без повторного Base64. Реальный статический egress IP хранится в deployment inventory, а не env; если он не обеспечен NAT/сетевой конфигурацией, `KEYCLOAK_OTP_MOCK_ENABLED=false` запрещён.
**Webhook-токены** (публичные callback, не service API): `BITRIX_APPLICATION_TOKEN`, `BITRIX_SYNC_WEBHOOK_TOKEN`.
## Namespace переменных Bitrix
@@ -317,16 +357,16 @@ presigned URL и CORS Selectel; path-style адресация не поддер
## Keycloak settings bridge для OTP
Product limits OTP (`otp.phone.*`) хранятся в `app_settings`, но Keycloak не получает прямой доступ к схеме `han_app`.
OTP settings (`otp.phone.*`) хранятся в `app_settings`, но Keycloak не получает прямой доступ к схеме `han_app`.
MVP-механизм:
1. `api-backend` читает публичные/служебные настройки из `app_settings` и кэширует их.
2. Для Keycloak SPI доступен internal endpoint `GET /internal/settings/v1/otp` в Docker/VPC-сети, защищённый service token.
3. Keycloak SPI читает `otp.phone.max_send_attempts_per_24h`, `otp.phone.min_seconds_between_attempts` и `otp.phone.max_verify_attempts` через этот endpoint с локальным cache TTL.
3. Keycloak SPI читает limits, `otp.phone.code_length`, `otp.phone.ttl_seconds` и `otp.phone.sms_order_timeout_ms` через этот endpoint с локальным cache.
4. При недоступности settings bridge SPI использует последнее валидное cache-значение; если cache пустой — fail-closed и не выдаёт OTP.
Счётчики попыток OTP остаются в зоне Keycloak/SPI, не в `api-backend`.
Challenge сохраняет snapshot TTL, длины кода и `settings_version`; изменение settings влияет только на новые challenges. Счётчики попыток OTP остаются в зоне Keycloak/SPI, не в `api-backend`.
## Разрешённые типы файлов чата (MVP)