Правки от GPT
This commit is contained in:
@@ -26,11 +26,11 @@ HAN Chat - приложение для мигрантов, где стартов
|
||||
## Пользовательские сценарии
|
||||
|
||||
1. Клиент открывает мобильное или web-приложение и видит главный экран с приветствием, популярными вопросами и полем ввода.
|
||||
2. Frontend определяет, нужна ли **новая UX-сессия**, и при необходимости отправляет событие **`session_start`** (см. «Аналитическая UX-сессия»). Клиент может изучить сервис без авторизации.
|
||||
2. Frontend определяет, нужна ли **новая UX-сессия**, но до JWT не вызывает backend write-endpoint: клиент может изучить сервис без авторизации через guest UI и `GET /api/v1/public/*`.
|
||||
3. Если у клиента сохранён **действующий refresh token**, frontend выполняет silent refresh **без OTP** (см. «Поток возврата пользователя»).
|
||||
4. Клиент нажимает популярный вопрос — frontend подставляет текст вопроса и **инициирует отправку сообщения** (тот же поток, что ручной ввод). Либо клиент вводит свой текст и отправляет.
|
||||
5. Если клиент не авторизован, перед отправкой первого сообщения frontend показывает pop-up с согласиями и запускает OTP (см. «Поток авторизации»).
|
||||
6. После успешной авторизации frontend с JWT вызывает **`POST /auth/bootstrap`** (в теле — принятые согласия): api-backend создаёт или находит локального пользователя по `keycloak_sub`, сохраняет согласия на `user_id`, создаёт или обновляет профиль; триггер App DB ставит задачу в `sync_queue` для `bitrix-sync`. Затем при необходимости — `POST /analytics/session-start`.
|
||||
6. После успешной авторизации frontend с JWT вызывает **`POST /api/v1/auth/bootstrap`** (в теле — принятые согласия): api-backend создаёт или находит локального пользователя по `keycloak_sub`, сохраняет согласия на `user_id`, создаёт или обновляет профиль; триггер App DB ставит задачу в `sync_queue` для `bitrix-sync`. Затем, если у frontend нет активной UX-сессии или она истекла, вызывается **`POST /api/v1/analytics/session-start`**.
|
||||
7. api-backend выполняет find-or-create диалога (см. «Создание диалога») и отправляет сообщение (текст популярного вопроса или введённый клиентом).
|
||||
8. Сообщение клиента проходит Message Safety и через Bitrix24 Local App направляется в Битрикс24 Open Lines.
|
||||
9. Ответ оператора из Битрикс24 Open Lines поступает через Bitrix24 Local App в api-backend и отображается в чате приложения.
|
||||
@@ -223,7 +223,7 @@ Frontend не должен:
|
||||
|
||||
- OTP-only регистрацию и вход;
|
||||
- OTP по номеру телефона; проверка кода — в Keycloak (заглушка `KEYCLOAK_OTP_MOCK_*` или SMS-провайдер, см. arch-04 и «Поток авторизации»);
|
||||
- продуктовые лимиты OTP (`otp.phone.*` из `app_settings`) через authenticator/SPI; счётчики попыток — в зоне Keycloak (Redis DB Keycloak/SPI или in-memory Keycloak), **не** в `api-backend`;
|
||||
- продуктовые лимиты OTP (`otp.phone.*` из `app_settings`) через authenticator/SPI и settings bridge `api-backend` (см. arch-04); счётчики попыток — в зоне Keycloak (Redis DB Keycloak/SPI или in-memory Keycloak), **не** в `api-backend`;
|
||||
- хранение учетных записей;
|
||||
- выдачу и обновление токенов (access + refresh);
|
||||
- настройку realm, clients, roles, policies;
|
||||
@@ -294,7 +294,7 @@ api-backend не решает, sync или async нужна проверка в
|
||||
|
||||
- UI главного экрана, популярные вопросы и публичный контент — через `GET /api/v1/public/*` (без JWT);
|
||||
- pop-up согласий показывается **до** OTP, но факт принятия хранится **только на клиенте** до получения tokens;
|
||||
- **`POST /consents`**, **`POST /analytics/session-start`** и остальные write/API чата — **только с JWT**;
|
||||
- **`POST /api/v1/consents`**, **`POST /api/v1/analytics/session-start`** и остальные write/API чата — **только с JWT**;
|
||||
- опциональный локальный `guest_session_id` (UUID в secure storage) может использоваться frontend для своей аналитики/идемпотентности UI, но **не** является auth и **не** открывает backend write-endpoint.
|
||||
|
||||
## Аналитическая UX-сессия (`ux_session_id`)
|
||||
@@ -326,7 +326,7 @@ api-backend не решает, sync или async нужна проверка в
|
||||
|
||||
1. Клиент открывает приложение. Пока нет JWT — гостевой UI; `session-start` не вызывается.
|
||||
2. Frontend проверяет наличие refresh token в secure storage.
|
||||
3. Если refresh token **действителен** — frontend запрашивает новый access token у Keycloak (Refresh Token Grant), **OTP не показывается**, затем при необходимости `POST /analytics/session-start`.
|
||||
3. Если refresh token **действителен** — frontend запрашивает новый access token у Keycloak (Refresh Token Grant), **OTP не показывается**, затем при необходимости начала новой UX-сессии вызывает `POST /api/v1/analytics/session-start`.
|
||||
4. Frontend работает как авторизованный пользователь (история, профиль, чат).
|
||||
5. Если refresh token **отсутствует или истёк** — клиент остаётся в гостевом режиме до сценария, требующего auth; при первом сообщении — «Поток авторизации» с OTP.
|
||||
|
||||
@@ -417,7 +417,7 @@ api-backend не решает, sync или async нужна проверка в
|
||||
**Общая ветка вердикта (оба типа):**
|
||||
|
||||
5. **`403 deny`**: API удаляет quarantine (если был файл), выставляет `safety_status=blocked`, `delivery_status=rejected`, возвращает клиенту безопасную ошибку; в Bitrix24 ничего не уходит.
|
||||
6. **`200 allow`**: API переносит файл в S3-data attachments (если был), сохраняет сообщение (`safety_status=allowed`, `delivery_status=delivered`), отправляет в Bitrix24, подтверждает клиенту; `Dialog.status` → `waiting_for_company`.
|
||||
6. **`200 allow`**: API переносит файл в S3-data attachments (если был), сохраняет сообщение (`safety_status=allowed`, `delivery_status=accepted`) и фиксирует задачу доставки в Open Lines. После успешной отправки через `bitrix-local-app` статус становится `delivery_status=delivered`, API подтверждает клиенту финальный результат; `Dialog.status` → `waiting_for_company`. Если Bitrix24/S3/dependency недоступны после allow, статус становится `delivery_status=failed`, клиент получает безопасную ошибку зависимости.
|
||||
7. **`203 pending` + `task_id`**: api-backend пишет checkpoint в `safety_tasks` и **регулярно синхронно** вызывает `GET /internal/safety/v1/messages/tasks/{task_id}` (backoff), пока не получит финальный вердикт или не истечёт `MESSAGE_SAFETY_TASK_POLL_MAX_SEC`. Пока идёт poll, **этот** клиентский `POST .../messages` ещё не завершён (соединение ждёт). Параллельные запросы других клиентов **не** блокируются — общей очереди анализа на api-backend нет.
|
||||
- финальный **`200 allow`** → как п. 6, затем ответ клиенту;
|
||||
- финальный **`403 deny`** → как п. 5, затем ответ клиенту;
|
||||
@@ -425,15 +425,22 @@ api-backend не решает, sync или async нужна проверка в
|
||||
|
||||
Клиент на `POST .../messages` получает **только финальный** результат (или ошибку инфраструктуры), не промежуточное «обрабатывается».
|
||||
|
||||
### Надёжность доставки и recovery
|
||||
|
||||
- Для доставки в Bitrix24 используется transactional outbox/checkpoint в App DB: запись `Message` и запись намерения доставки фиксируются атомарно, а повторная отправка в `bitrix-local-app` идемпотентна по `message_id` / `Idempotency-Key`.
|
||||
- `delivery_status=accepted` означает, что API принял сообщение и завершил safety allow, но ещё не получил подтверждение доставки в Open Lines. `delivery_status=delivered` выставляется только после успешного ответа `bitrix-local-app` о приёме сообщения для Bitrix24 Open Lines.
|
||||
- Recovery по `han_app.safety_tasks` восстанавливает только сценарии, где Message Safety вернул `203 pending` и клиентский запрос оборвался из-за timeout/crash. Recovery job повторно опрашивает `message-safety` по `task_id`, затем идемпотентно выполняет promote/delete quarantine и обновляет `Message`/`MessageAttachment`.
|
||||
- Объекты в S3-quarantine не удаляются при timeout safety до финального verdict; orphan-cleanup удаляет только просроченные объекты без активного `safety_tasks` или attachment metadata.
|
||||
|
||||
## Поток работы с чатом: Битрикс24 -> клиент
|
||||
|
||||
1. Оператор отвечает клиенту в Битрикс24 Open Lines.
|
||||
2. Битрикс24 отправляет `ONIMCONNECTOR*` webhook/event в `bitrix-local-app`.
|
||||
3. `bitrix-local-app` проверяет `application_token`, нормализует payload и сохраняет idempotent inbox.
|
||||
4. `bitrix-local-app` обогащает событие данными из `dialog_sessions` и forward-ит в API, если `BITRIX_API_FORWARD_URL` включен.
|
||||
4. `bitrix-local-app` обогащает событие данными из `dialog_sessions` и forward-ит в API, если `BITRIX_API_FORWARD_URL` включен. При недоступности API событие остаётся во внутреннем inbox, повторяется с backoff и после исчерпания retry попадает в DLQ; дубликаты определяются по `(external_chat_id, bitrix_message_id)`.
|
||||
5. api-backend находит локальный диалог по `external_chat_id` (= `dialog_id`, см. [`arch-00-glossary.md`](arch-00-glossary.md)).
|
||||
6. api-backend сохраняет входящее сообщение в App DB (`sender_type=company`, `delivery_status=delivered`). Файл оператора (если есть) — в бакет **S3-data attachments** (`han-chat-attachments`) с metadata в `MessageAttachment`; бакет **documents** зарезервирован для документов компании в профиле (post-MVP). По факту сообщения API обновляет `Dialog.status`: входящее от оператора → `waiting_for_client`, исходящее от клиента → `waiting_for_company` (значения — arch-00).
|
||||
7. `bitrix-local-app` подтверждает доставку в Bitrix24 через `imconnector.send.status.delivery`.
|
||||
7. `bitrix-local-app` подтверждает доставку в Bitrix24 через `imconnector.send.status.delivery` после успешного сохранения события в api-backend или идемпотентного duplicate-ack.
|
||||
8. api-backend публикует событие для frontend через **WebSocket** (`WS /api/v1/realtime`). Если realtime недоступен, frontend получает сообщение через polling `GET /api/v1/dialogs/{dialog_id}/messages?after=...`.
|
||||
9. Frontend отображает сообщение оператора в чате.
|
||||
10. При получении от `bitrix-local-app` доменного события `dialog.closed` (Bitrix24 `ONIMCONNECTORDIALOGFINISH`) API переводит `Dialog.status` в `closed`.
|
||||
@@ -517,7 +524,7 @@ App DB — **локальный кэш** для UI. Двусторонний syn
|
||||
- Доступ к профилю, диалогам, сообщениям, файлам и документам ограничен текущим `user_id`.
|
||||
- Все запросы, содержащие в себе ссылку на сущность, которая относится к конкретному пользователю (ИД продукта, услуги, чата, документа и тп), проверяются backend_api на соответствие тому пользователю, от которого пришел запрос.
|
||||
- Все публичные id создаются в формате UUID.
|
||||
- Сервисные API защищаются внутренней сетью Docker/VPC плюс service token (перечень переменных — [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), «Service tokens (internal API)»).
|
||||
- Сервисные API защищаются внутренней сетью Docker/VPC плюс service token (канонический контракт — [`arch-02-api-contracts.md`](arch-02-api-contracts.md), «Service tokens (internal API)»; значения переменных — [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md)).
|
||||
- Rate limits применяются минимум на двух уровнях: edge-лимиты в `nginx` и пользовательские лимиты в API с состоянием в Redis.
|
||||
- Входящие сообщения пользователя: синхронный `POST /internal/safety/v1/messages/check` → при `203` api-backend синхронно поллит `task_id` до финального `200`/`403` (или timeout); без очереди анализа на api-backend.
|
||||
- Файлы пользователя до финального `allow` только в S3-quarantine; в S3-data — после `200 allow`.
|
||||
|
||||
Reference in New Issue
Block a user