# Конфигурация проекта message-hub
# Версия: 0.1.0

Документ описывает все переменные окружения и способы конфигурирования сервиса.

## Способы конфигурирования

Сервис настраивается **через переменные окружения**. Разбор выполняется в `src/config/` через библиотеку [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/). Настройки разбиты на несколько классов, каждый со своим префиксом:

- `Settings` (`src/config/__init__.py`) — общий класс приложения, префикс `SETTINGS_`;
- `DBSettings` (`src/config/db.py`) — префикс `DB_`;
- `KafkaSettings` (`src/config/kafka.py`) — префикс `KAFKA_`;
- `RedisSettings` (`src/config/redis.py`) — префикс `CACHE_`;
- `ServiceConfig` и наследники (`src/config/sarex.py`) — префиксы `SAREX_`, `PM_`, `ISSUES_`, `BI_`, `EAV_`, `PDF_CONVERTER_`;
- `S3Settings` (`src/config/s3.py`) — префикс `S3_`;
- `MailerSettings` (`src/config/mailer.py`) — префикс `MAILER_`;
- `LoggerSettings` (`src/config/logger.py`) — префикс `LOG_`.

Особенности разбора:

- у каждого класса задан `env_file='.env'` и `extra='ignore'` — при наличии файла `.env` в рабочей директории он загружается автоматически, лишние переменные игнорируются;
- вложенных секций через разделитель нет — каждая группа настроек читается отдельным классом по своему префиксу;
- поле `VERIFY_SSL` в `S3Settings` и во всех `ServiceConfig` объявлено с `alias='SETTINGS_VERIFY_SSL'` — то есть единая переменная `SETTINGS_VERIFY_SSL` управляет проверкой TLS-сертификатов сразу для S3 и всех HTTP-клиентов внешних сервисов;
- у большинства полей есть значения по умолчанию, поэтому формально сервис стартует и без `.env`, но с дефолтными (локальными) адресами БД, Kafka, Redis и сервисов.

Отдельного конфиг-файла (yaml/toml) у приложения нет.

Источники переменных по способам запуска:

| Способ запуска | Откуда берутся переменные |
| --- | --- |
| Локально (бинарник) | Файл `.env` в рабочей директории (загружается `pydantic-settings`) и/или переменные окружения процесса |
| Локально (контейнеры) | `docker-compose.yaml`: блок `environment` для сервиса `message-hub` (`PYTHONPATH`, `KAFKA_HOST`, `KAFKA_PORT`) |
| Kubernetes (Helm) | `.helm/values.yaml`: блок `universal-chart.services.message-hub.envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов); базовый чарт — `universal-chart` |
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`) и build-args |

Запуск процесса (`docker/entrypoint.sh`): единый ASGI-процесс поднимается через `gunicorn` с воркерами `uvicorn.workers.UvicornWorker` на `0.0.0.0:8000`:

```
gunicorn -w $WORKERS -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 --timeout $WORKER_TIMEOUT --access-logfile - main:app
```

Приложение `main:app` (`src/main.py`) объединяет в одном ASGI-приложении: FastStream-брокер Kafka (потребители сообщений), HTTP-роуты health-проверок и Socket.IO-сервер (`AsyncServer` поверх `AsyncRedisManager`). Отдельных точек входа для воркеров/крон-задач нет — `pyproject.toml` не содержит `[project.scripts]`.

## Переменные приложения

### App (`SETTINGS_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SETTINGS_TOPICS` | dict (JSON) | `{}` | Соответствие логических топиков (`planning`/`assets`/`issues`) реальным именам топиков Kafka. Валидатор запрещает ключи вне набора `assets`/`planning`/`issues` |
| `SETTINGS_DEBUG` | bool | `False` | Режим отладки |
| `SETTINGS_VERIFY_SSL` | bool | `True` | Проверка TLS-сертификатов для S3 и всех HTTP-клиентов внешних сервисов (общий флаг через alias) |
| `SETTINGS_WORKER_TIMEOUT` | int | `30` | Таймаут воркера (поле `WORKER_TIMEOUT` класса `Settings`) |
| `SETTINGS_RETRY_DELAY` | int | `3` | Стартовая задержка (сек) между повторами обработки сообщения Kafka; удваивается на каждой попытке |
| `SETTINGS_MAX_RETRIES` | int | `3` | Число попыток обработки сообщения Kafka перед `ack` |
| `SETTINGS_REQUEST_RETRIES` | int | `2` | Число повторов HTTP-запросов к внешним сервисам |
| `SETTINGS_REQUEST_DELAY` | int | `2` | Задержка (сек) между повторами HTTP-запросов |
| `SETTINGS_MESSAGE_SKIP_AGE` | int | `300` | Возраст сообщения (сек), старше которого оно пропускается |
| `SETTINGS_CACHE_EXPIRATION` | int | `120` | TTL (сек) ключей присутствия пользователей в Redis (WebSocket) |
| `SETTINGS_SENDER` | string | `noreply@sarex.io` | Адрес отправителя по умолчанию |

### Логирование (`LOG_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `LOG_LEVEL` | string | `INFO` | Уровень логирования (стандартные уровни `logging`) |
| `LOG_FORMAT` | string | `[%(asctime)s] [%(levelname)s] [%(filename)s]: %(message)s` | Формат строк лога |

### Database (`DB_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DB_HOST` | string | `''` | Хост PostgreSQL |
| `DB_PORT` | int | `5432` | Порт PostgreSQL |
| `DB_DATABASE` | string | `''` | Имя базы данных |
| `DB_USERNAME` | string | `''` | Пользователь БД |
| `DB_PASSWORD` | string | `''` | Пароль пользователя БД |
| `DB_DIALECT` | string | `postgresql+psycopg` | Диалект/драйвер SQLAlchemy. Итоговый DSN собирается в `db.url` |

### Kafka (`KAFKA_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `KAFKA_HOST` | string | `localhost` | Хост брокера |
| `KAFKA_PORT` | int | `9092` | Порт брокера |
| `KAFKA_USERNAME` | string \| null | `None` | Логин SASL |
| `KAFKA_PASSWORD` | string \| null | `None` | Пароль SASL |
| `KAFKA_SECURITY_PROTOCOL` | string | `PLAINTEXT` | Протокол безопасности: `PLAINTEXT`/`SSL`/`SASL_PLAINTEXT`/`SASL_SSL`. При `SSL`/`SASL_SSL` используется SSL-контекст |
| `KAFKA_SASL_MECHANISM` | string \| null | `None` | Механизм SASL. Обрабатываются `PLAINTEXT` и `SCRAM-SHA-512` |
| `KAFKA_SSL_CAFILE` | string \| null | `None` | Путь к CA-сертификату для SSL-контекста |

### Redis / кеш (`CACHE_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CACHE_HOST` | string | `localhost` | Хост Redis |
| `CACHE_PORT` | int | `6378` | Порт Redis |
| `CACHE_PASSWORD` | string \| null | `None` | Пароль Redis |
| `CACHE_SSL` | bool | `False` | Подключение по TLS (`rediss://`) |
| `CACHE_SSL_CA_CERTS` | string \| null | `None` | Путь к CA-сертификату Redis |

> Redis используется как менеджер состояния Socket.IO (`AsyncRedisManager`) и как хранилище присутствия пользователей в проектах.

### S3 (`S3_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `S3_HOST` | string | `https://storage.yandexcloud.net` | Endpoint S3 (`endpoint_url`) |
| `S3_LOGIN` | string | `''` | Access key |
| `S3_PASSWORD` | string | `''` | Secret key |
| `S3_BUCKET` | string | `sarex-media-storage` | Бакет по умолчанию |
| `SETTINGS_VERIFY_SSL` | bool | `True` | Проверять TLS-сертификат (общий флаг, см. App) |

### HTTP-клиенты внешних сервисов

Все клиенты наследуют общий класс `ServiceConfig` с полями `HOST`, `TIMEOUT` и общим флагом `VERIFY_SSL` (через alias `SETTINGS_VERIFY_SSL`). Значения по умолчанию: `HOST=http://localhost:8001`, `TIMEOUT=60`.

| Секция / префикс | Назначение |
| --- | --- |
| `SAREX_*` | Sarex backend (получение токенов клиентов и пр.) |
| `PM_*` | PM backend (синхронизация задач, автопланирование) |
| `ISSUES_*` | Сервис issues (типы задач, модели статусов) |
| `BI_*` | BI backend (синхронизация значений аналитики) |
| `EAV_*` | EAV-сервис (ассеты и атрибуты) |
| `PDF_CONVERTER_*` | Конвертер HTML → PDF (export-project) |

Для каждого — две переменные, напр. для PM:

| Переменная | Тип | Назначение |
| --- | --- | --- |
| `PM_HOST` | string | Базовый URL сервиса |
| `PM_TIMEOUT` | int | Таймаут запроса (сек) |

### Mailer (`MAILER_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `MAILER_HOST` | string | `http://localhost:8001` | Базовый URL сервиса рассылок |
| `MAILER_PREFIX` | string | `/api/v1` | Префикс маршрутов сервиса рассылок |
| `MAILER_TIMEOUT` | int | `60` | Таймаут запроса (сек) |

## Переменные инфраструктуры, сборки и запуска

Не читаются классами настроек приложения, но участвуют в запуске/сборке/деплое.

| Переменная | Где используется | Назначение |
| --- | --- | --- |
| `PYTHONPATH` | `docker-compose.yaml`, Helm `envs` | Каталог исходников (`src`) |
| `WORKERS` | `docker/entrypoint.sh`, Helm `envs` | Число воркеров gunicorn (по умолчанию `2`) |
| `WORKER_TIMEOUT` | `docker/entrypoint.sh`, Helm `envs` | Таймаут воркера gunicorn (`--timeout`) |
| `CI_COMMIT_SHORT_SHA` | `docker/Dockerfile` (build-arg через `BUILD_ARGS`) | Идентификатор сборки |

## Переменные из Helm-чарта (`.helm/values.yaml`)

Сервис деплоится через зависимость `universal-chart`. Обычные значения задаются в блоке `universal-chart.services.message-hub.envs` для окружений `stage`/`preprod`/`production` (различаются адресами БД, Kafka, Redis, сервисов, именами топиков, числом реплик и таймаутами).

Значения из секретов (блок `secretEnvs`, монтируются как env через `secretKeyRef`):

| Переменная | Секрет (`_default` / `production`) | Ключ (`secretKey`) |
| --- | --- | --- |
| `KAFKA_USERNAME` | `message-hub-kafka-secret` / `kafka-secret` | `username` |
| `KAFKA_PASSWORD` | `message-hub-kafka-secret` / `kafka-secret` | `password` |
| `DB_USERNAME` | `pm-postgresql-secret` / `postgres-pm-secret` | `user` |
| `DB_PASSWORD` | `pm-postgresql-secret` / `postgres-pm-secret` | `password` |
| `CACHE_PASSWORD` | `cache-secret-pm` / `cache-secret` | `password` |
| `S3_LOGIN` | `planning-s3-secret` / `s3-secret` | `username` |
| `S3_PASSWORD` | `planning-s3-secret` / `s3-secret` | `password` |
| `S3_BUCKET` | `planning-s3-secret` / `s3-secret` | `bucket` |
| `S3_HOST` | `planning-s3-secret` / `s3-secret` | `host` |

Помимо env, чарт монтирует CA-сертификат из секрета `kafka-secret` (ключ `ssl_cafile`) как файл `/opt/ssl/ca.pem` (том `kafka-ca-volume`, `readOnly`) — на него указывают `KAFKA_SSL_CAFILE` и `CACHE_SSL_CA_CERTS` в конфигурациях окружений.

Health-пробы (`.helm/values.yaml`): liveness `GET /health/live`, readiness `GET /health/ready`, порт `8000`.

## Переменные в CI (`.gitlab-ci.yml`)

Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`) и переключает окружение по ветке/тегу через `workflow.rules`:

| Условие | STAND | Namespace | CHART_VERSION |
| --- | --- | --- | --- |
| ветка `stage` | `stage` | `planning` | `0.0.1-stage` |
| ветка `master` | `preprod` | `message-hub-preprod` | `0.0.1-preprod` |
| тег (`CI_COMMIT_TAG`) | `production` | `message-hub-prod` | `0.0.1-prod` |

Ключевые переменные пайплайна: `SERVICE_NAME=message-hub`, `DOCKERFILE_PATH=./docker/Dockerfile`, `BUILD_ARGS`, `CI_TRIGGER_SOURCE=app`, `RELEASE_NAME`, `CHART_NAME`, `CHART_VERSION`, `K8S_HUSTLER_BRANCH`, `HELM_SET_ARGS` (`--set universal-chart...`), флаг `ENABLE_BUILD_IMAGE`. Отдельные job'ы `linter` (`ruff check` / `ruff format --check`) и `typechecker` (`mypy src`).

## Замечания и потенциальные проблемы

- Единая переменная `SETTINGS_VERIFY_SSL` управляет проверкой TLS сразу для S3 и всех шести HTTP-клиентов (alias у поля `VERIFY_SSL`). Отдельно на клиент это не настраивается.
- Поле `WORKER_TIMEOUT` есть и в классе `Settings` (читается как `SETTINGS_WORKER_TIMEOUT`, дефолт `30`), и как самостоятельная переменная `WORKER_TIMEOUT` для gunicorn (`entrypoint.sh`, Helm). Это разные переменные — не перепутайте.
- `SETTINGS_TOPICS` валидируется: допустимы только ключи `assets`, `planning`, `issues`. Прочие ключи вызывают ошибку старта. Если ключ отсутствует, соответствующий потребитель подписывается на пустое имя топика.
- Файл `.env.example` в репозитории сервиса не содержит части переменных (сервисы `PM_/ISSUES_/BI_/EAV_/PDF_CONVERTER_`, `MAILER_`, `LOG_`, ряд `SETTINGS_*`) — при реальном запуске задавайте их явно (полный перечень — в данном документе и в `.env.example` рядом).
- У большинства полей есть дефолты (локальные адреса), поэтому при пустом окружении сервис поднимется, но будет ходить на `localhost` — для рабочих окружений значения задаются через Helm.

## Минимальный набор для локального запуска

Kafka поднимается через `docker-compose.yaml` (сервисы `kafka`, `kafka-ui`); PostgreSQL и Redis — внешние. Минимально стоит задать (с учётом префиксов):

- `SETTINGS_TOPICS` — карта логических топиков в реальные;
- `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`;
- `KAFKA_HOST`, `KAFKA_PORT` (для docker-compose — `kafka:9092`);
- `CACHE_HOST`, `CACHE_PORT` (+ `CACHE_PASSWORD`/`CACHE_SSL` при необходимости);
- `S3_HOST`, `S3_LOGIN`, `S3_PASSWORD`, `S3_BUCKET`;
- `HOST` для внешних сервисов, которые реально используются (`SAREX_`, `PM_`, `ISSUES_`, `BI_`, `EAV_`, `PDF_CONVERTER_`, `MAILER_`);
- `SETTINGS_VERIFY_SSL` (`0` локально, если сертификаты самоподписанные).

Готовые значения-примеры приведены в `.env.example` рядом с этим документом.
