Добавлены уведомления

This commit is contained in:
mi
2026-07-27 17:36:53 +03:00
parent a072005164
commit 958fba5f3e
149 changed files with 6371 additions and 110 deletions
+29 -3
View File
@@ -32,6 +32,7 @@
- аудит, метрики, трассировку, health и readiness;
- чтение `app_settings`, `text_resources`, `popular_questions`;
- internal settings bridge для Keycloak SPI.
- Notification Center G/P: каталог, public/JWT/internal API, lifecycle, документы, realtime и фоновые задачи.
### 2.2. Сервис не отвечает за
@@ -150,6 +151,8 @@ api-backend/
safety_recovery.py
quarantine_cleanup.py
inbound_attachment.py
notification_expire.py
notification_draft_cleanup.py
settings.py
alembic/
tests/
@@ -455,6 +458,14 @@ Complete request: `{"checksum":"sha256:<64-lowercase-hex>"}`. Response `200` в
Init и complete должны быть idempotent по состоянию attachment; повтор complete с тем же checksum возвращает прежний результат, с другим — `409 resource_state_conflict`.
### 6.8. Notification Center
Контракты путей и DTO — arch-02 и `notification-requirements.md`. Реализация читает `notification_types` и реестры CTA/кнопок/цветов; ветвление по `notification_type` запрещено. Home/center/counter применяют серверные лимиты 7/15, эффективный приоритет `COALESCE(priority_override, type.priority)` и ownership.
Действие скрытия всегда ставит `visibility='hidden'`. Если `date_expired` уже задано, оно сохраняется; TTL вида/default устанавливает `date_expired=now()+N days` только при `NULL`. Первое скачивание любого связанного документа при `hide_on_document_download=true` атомарно применяет этот эффект один раз.
`install_app_prompt` возвращает только внешнее действие открытия `instruction_url` в новой вкладке. Режимы iframe/modal и allow-list для них отсутствуют.
## 7. Internal endpoints
### 7.1. Inbox Open Lines
@@ -524,6 +535,13 @@ Private network + Bearer `KEYCLOAK_SETTINGS_BRIDGE_TOKEN`.
Все вызовы: private network, `Authorization: Bearer <BITRIX_LOCAL_APP_INTERNAL_TOKEN>`, `X-Request-ID`, `traceparent`, idempotency по `message_id`.
### 7.4. Internal Notifications
- `POST /internal/notifications/v1/notifications` — Create;
- `POST /internal/notifications/v1/notifications/cancel` — Cancel.
Bearer token отдельный для каждого `source`; secret приходит из `NOTIFICATIONS_TOKEN_<SOURCE>`, в `notification_sources` хранится только hash, сравнение constant-time. `producer_test`/`NOTIFICATIONS_TOKEN_PRODUCER_TEST` служат smoke API. `(source, external_id)` уникальна бессрочно: одинаковый fingerprint → `200`, другой → `409 notification_conflict`.
## 8. JWT, JWKS и phone claims
### 8.1. Validation
@@ -972,8 +990,8 @@ Outbox worker и synchronous first attempt используют один dispatc
```json
{"type":"connected","server_time":"2026-07-09T12:00:00Z"}
{"type":"subscribe","dialog_ids":["uuid"]}
{"type":"subscribed","dialog_ids":["uuid"]}
{"type":"subscribe","dialog_ids":["uuid"],"notifications":true}
{"type":"subscribed","dialog_ids":["uuid"],"notifications":true}
```
Events:
@@ -981,9 +999,10 @@ Events:
- `message.new` с `MessageResponse`;
- `message.status`;
- `dialog.status`;
- `notification.created`, `notification.updated`, `notification.closed` через `han:rt:user:{user_id}`, включая эхо инициатору;
- `ping`; client отвечает `pong`.
Каждый subscribe проверяет ownership всех dialogs; чужие id не раскрываются. Лимиты: max connections/user, max subscriptions/connection, max frame bytes, subscribe rate. Slow consumer: bounded queue; при переполнении connection закрывается с retryable code, клиент восстанавливается polling.
`notifications` опционально и по умолчанию `false`. Каждый subscribe проверяет ownership всех dialogs; чужие id не раскрываются. Лимиты: max connections/user, max subscriptions/connection, max frame bytes, subscribe rate. Slow consumer: bounded queue; при переполнении connection закрывается с retryable code, клиент восстанавливается polling.
### 17.2. Delivery semantics
@@ -1025,6 +1044,7 @@ Runtime refresh: poll `MAX(updated_at)` каждые 30 секунд; новый
- `KEYCLOAK_SETTINGS_BRIDGE_TOKEN`;
- `SELECTEL_S3_ENDPOINT_URL`, три bucket names, write access key/secret;
- `OTEL_EXPORTER_OTLP_ENDPOINT`.
- `NOTIFICATIONS_TOKEN_PRODUCER_TEST` и последующие `NOTIFICATIONS_TOKEN_<SOURCE>`; plaintext не сохраняется в БД/логах.
`SELECTEL_S3_QUARANTINE_READ_*` принадлежит `message-safety`, не должен передаваться контейнеру API. Новые env сначала документируются в arch-04.
@@ -1039,6 +1059,8 @@ Runtime refresh: poll `MAX(updated_at)` каждые 30 секунд; новый
| create dialog/message | user + dialog + IP |
| attachment init/complete | user + dialog |
| download URL | user + resource group |
| notifications read/action/upload | user + IP / user / user |
| notifications public | IP hash |
| WS connect/subscribe | user + IP |
| internal inbox/settings | service identity + source network |
@@ -1119,6 +1141,8 @@ Graceful shutdown прекращает принимать новые requests,
- `attachment.upload_initialized/completed/promoted/rejected`;
- `attachment.download_url_issued`;
- `document.download_url_issued`;
- `notification.created/read/hidden/cta_invoked/button_pressed/closed`;
- `notification.document.download_url_issued`, `notification.documents.submitted`, `notification.expired_batch`;
- `openlines.inbox_applied`;
- повторные severe rate limit violations.
@@ -1199,6 +1223,8 @@ Startup:
8. start workers;
9. mark ready.
Notification expire запускается ежедневно в `notification.expire_job.run_at` с PostgreSQL advisory lock и set-based update; массовые WS-события не публикует. Cleanup удаляет просроченные drafts и соответствующие S3-объекты идемпотентно. Отдельные Compose-процессы используют зарегистрированные scripts `han-notification-expire-worker` и `han-notification-draft-cleanup-worker`; общий `han-cleanup-worker` сохраняет прежнюю очистку quarantine.
Nginx маршрутизирует `/api/*`, включая WS `/api/v1/realtime`. Для message POST `proxy_read_timeout >= MESSAGE_SAFETY_TASK_POLL_MAX_SEC + 30s`. Internal paths наружу не маршрутизируются.
## 25. Ключевые user flows