HAN Chat API backend
FastAPI-сервис публичного API, internal Open Lines/settings API, WebSocket realtime, Message Safety orchestration, S3 lifecycle и PostgreSQL workers.
Запуск
Python 3.12+:
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]"
alembic upgrade head
uvicorn app.main:app --host 0.0.0.0 --port 8000
Миграции выполняются отдельным deployment step. Приложение не применяет DDL при старте.
Workers запускаются независимо:
han-delivery-worker
han-safety-worker
han-cleanup-worker
han-notification-expire-worker
han-notification-draft-cleanup-worker
Переменные окружения
Сервис читает только инфраструктурные параметры и секреты из корневого backend/.env.
Локальный .env не коммитится. Business settings создаются миграцией в
han_app.app_settings.
Обязательны:
APP_ENV,API_PORT,LOG_LEVEL,DATABASE_URL;REDIS_URL,REDIS_REALTIME_URL;KEYCLOAK_PUBLIC_URL,KEYCLOAK_INTERNAL_URL,KEYCLOAK_REALM,KEYCLOAK_AUDIENCE;MESSAGE_SAFETY_URL,MESSAGE_SAFETY_SERVICE_TOKEN,MESSAGE_SAFETY_CA_FILE,MESSAGE_SAFETY_API_PREFIX=/internal/safety/v2,MESSAGE_SAFETY_POST_TIMEOUT_SEC,MESSAGE_SAFETY_TASK_POLL_INTERVAL_SEC,MESSAGE_SAFETY_TASK_POLL_MAX_SEC,MESSAGE_SAFETY_CIRCUIT_FAILURE_THRESHOLD,MESSAGE_SAFETY_CIRCUIT_OPEN_SEC;BITRIX_LOCAL_APP_BASE_URL,BITRIX_LOCAL_APP_INTERNAL_TOKEN,BITRIX_API_INBOX_TOKEN,BITRIX_LOCAL_APP_HTTP_TIMEOUT_SEC,BITRIX_LOCAL_APP_CIRCUIT_FAILURE_THRESHOLD,BITRIX_LOCAL_APP_CIRCUIT_OPEN_SEC;KEYCLOAK_SETTINGS_BRIDGE_TOKEN;SELECTEL_S3_ENDPOINT_URL,SELECTEL_S3_BUCKET_DOCUMENTS,SELECTEL_S3_BUCKET_ATTACHMENTS,SELECTEL_S3_BUCKET_QUARANTINE,SELECTEL_S3_ACCESS_KEY,SELECTEL_S3_SECRET_KEY;CURSOR_HMAC_SECRET— случайный секрет не короче 32 байт;NOTIFICATIONS_TOKEN_PRODUCER_TEST— отдельный bearer token тестового продюсера Notification Center; в БД синхронизируется только SHA-256 hash;OTEL_EXPORTER_OTLP_ENDPOINT— опциональный endpoint collector.
Токены генерируются openssl rand -hex 32. MESSAGE_SAFETY_SERVICE_TOKEN
сохраняется как caller secret API backend; S3 credentials сервису Safety не
передаются. В production подключение PostgreSQL должно использовать TLS. Target
задаётся через MESSAGE_SAFETY_URL=https://<private-vm2-name>:8443
(processing.internal — только пример), API prefix /internal/safety/v2;
certificate проверяется по CA из
MESSAGE_SAFETY_CA_HOST_PATH, plaintext HTTP запрещён.
Smoke-сценарий producer_test: отправить POST /internal/notifications/v1/notifications с Authorization: Bearer $NOTIFICATIONS_TOKEN_PRODUCER_TEST, source=producer_test и уникальным
external_id; повтор того же тела вернёт 200. Затем передать ту же пару
source/external_id в POST /internal/notifications/v1/notifications/cancel
с close_reason=cancelled; повторная отмена также вернёт 200.
Проверки
ruff check .
ruff format --check .
mypy app
pytest
/health/live проверяет процесс. /health/ready проверяет критические
зависимости read API. Remote Message Safety не выключает чтение/общую readiness:
send endpoint отдельно проверяет требуемую capability и fail-closed возвращает
503, если ВМ2 недоступна.
Публичная конфигурация мобильных обновлений
GET /api/v1/public/app-config возвращает строгий объект mobile_update с
политиками google_play, rustore и app_store. Для включённого магазина
обязательны latest_build, minimum_build, latest_version и HTTPS store_url;
minimum_build не может превышать latest_build. Для отключённого магазина
эти поля возвращаются как null. Опциональное release_notes также возвращается
как null, если в settings задана пустая строка. Канонический URL RuStore:
https://www.rustore.ru/catalog/app/ru.han.chat.
Ответ содержит ETag, поддерживает If-None-Match с ответом 304 и в
production-like конфигурации кэшируется клиентом и nginx 60 секунд. Изменения
политики применяются через штатный идемпотентный seed-settings; некорректные
пороги, типы и URL блокируют загрузку settings.