Files

98 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# bitrix-sync
Изолированный Python 3.12 сервис durable-синхронизации Contact между `han_app` и
Битрикс24. Сервис не участвует в Open Lines и не имеет HTTP-зависимости от
`api-backend`.
## Entrypoints
- `han-bitrix-sync-api` — health, internal status и два bounded robot receiver;
- `han-bitrix-sync-worker` — queue/webhook/rebind workflows с lease fencing;
- `han-bitrix-sync-reconciliation` — один advisory-lock incremental run;
- `alembic upgrade head` — отдельная контролируемая миграция, не startup DDL.
Disabled mode требует только `BITRIX_SYNC_ENABLED=false` и
`BITRIX_SYNC_MODE=disabled`, не читает БД и не принимает webhook. Full mode
валидирует весь каталог secret files, portal identity, custom fields, HTTPS host
lock и непустой CIDR allow-list до startup.
## Наблюдаемость
JSON stdout включён всегда. Если задан стандартный
`OTEL_EXPORTER_OTLP_ENDPOINT`, traces, metrics и логи дополнительно отправляются
напрямую по OTLP/gRPC через bounded batch queues. Ошибка инициализации, экспорта
или остановки телеметрии не меняет readiness и не останавливает API, worker или
reconciliation. Процессы различаются как `bitrix-sync-api`,
`bitrix-sync-worker` и `bitrix-sync-reconciliation`; namespace остаётся
`han-chat`.
FastAPI (кроме `/health/live`), SQLAlchemy и HTTPX инструментированы. Перед
stdout и OTLP выполняется application redaction: query/token/credential URL,
headers, payload, PII, object keys и SQL не экспортируются. Бизнес-метрики
используют только закрытые множества labels; UUID и внешние идентификаторы в
labels/spans не записываются.
## Границы безопасности
- CRM credential URL используется как единый секрет; redirect выключен, TLS
проверяется, REST method выбирается только из закрытого allow-list.
- Receiver принимает только `application/x-www-form-urlencoded` с bounded
content length, числом и длиной полей. Query token сравнивается constant-time.
- nginx должен перезаписывать `X-Real-IP` из TCP peer, проверять CIDR до proxy и
не логировать `$request_uri`, args или body. Контейнер receiver недоступен
напрямую.
- DB хранит только hash CRM-master значений в snapshot; safe command projection
не содержит PII. URL credential, form body и token не логируются.
- Запись CRM-master полей выполняется в одной транзакции после
`SET LOCAL han.sync_suppress='true'`.
## Локальные проверки
Лёгкие проверки, не требующие Docker, сервиса или реального PostgreSQL:
```text
python -m pytest
python -m ruff check app tests
```
PostgreSQL integration и Bitrix contract suites намеренно являются внешними
gates: локальный managed PostgreSQL не поднимается Compose-файлом.
## External gates до `BITRIX_SYNC_ENABLED=true`
1. Применить migrations migration-role и проверить grants runtime-role.
2. На disposable managed PostgreSQL проверить concurrent `SKIP LOCKED`,
lease expiry/fencing, active mapping uniqueness, rebind partial failure,
transaction-local GUC без утечки и crash после CRM success.
3. Подтвердить на целевом портале wire-контракты `duplicate.findbycomm`,
Contact add/get/update, mixed `batch`, custom fields, enum dictionary и
`crm.item.list` с `opened=1`, registration REST field `=1`.
4. Заполнить и активировать валидную `business_alerts` settings version:
entity/category/stage/field IDs и responsible party. Placeholder `null`
запрещает alert receiver.
`value_json` настройки `business_alerts` использует ключи `entity_type_id`,
`category_id`, `stage_new`, опциональный `responsible_id` и объект `field_ids`.
В `field_ids` REST-имена пользовательских полей сопоставляются ключам
`alert_number`, `fingerprint`, `alert_type`, `severity`, `app_user_id`,
`selected_external_id`, `occurrence_count`,
`first_occurred_at`, `last_occurred_at`, `workflow_id`.
Конфликтующие Contact передаются в стандартное поле `contactIds`, а значение
`BITRIX_SYNC_CONTACT_SOURCE` — в стандартное поле `sourceId`.
5. Проверить least-privilege credential negative tests; credential администратора
запрещён.
6. Валидировать nginx exact routes, no-redirect HTTP policy, body/rate limits,
version-controlled CIDR и отсутствие query/body в access/error/traces.
7. Запустить synthetic webhook с реальным robot form contract, затем убедиться,
что durable inbox commit предшествует `202`.
8. Выполнить 10k incremental reconciliation/load gate, webhook-loss recovery,
429/5xx/TLS/DNS/timeout/uncertain-create и restart-at-each-step tests.
9. Проверить container image digest, dependency/SBOM/vulnerability scan и
compose hardening; root Compose ВМ2 подключает этот fragment отдельно.
10. Зафиксировать cutover watermark, отменить только pre-cutover active tasks,
выполнить disabled preflight и затем controlled enablement.
`compose.fragment.yaml` — сервисный фрагмент, не root Compose и не команда
развёртывания. Reconciliation entrypoint выполняет один run; расписание задаёт
root-owned scheduler/deployment layer.