# Конфигурация проекта flows-backend Документ описывает все переменные окружения и способы конфигурирования сервиса. ## Способы конфигурирования Сервис настраивается **только через переменные окружения**. Разбор выполняется в `src/flow/config.py` через библиотеку [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) (класс `Settings` и набор вложенных классов `*Settings`). Особенности разбора: - у верхнеуровневого класса `Settings` **префикса нет** и не задан `env_nested_delimiter` — его собственные поля задаются переменными с именем поля в верхнем регистре (напр. `BASE_HOST`, `SERVICE_PORT`, `PROXY_PATH_PREFIX`); - каждая вложенная секция — это **отдельный класс** `BaseSettings` со своим `env_prefix` (`class Config: env_prefix = "..."`), который читает переменные окружения независимо. Поэтому переменные «плоские» с префиксами: `PG_HOST`, `DJANGO_HOST`, `RABBITMQ_PORT`, `TRACING_USE` и т.д. — двойного подчёркивания для вложенности здесь нет; - отсутствие обязательного поля без дефолта приводит к ошибке старта; большинство полей приложения имеют дефолты (см. таблицы ниже). Отдельного конфиг-файла (yaml/toml) у приложения нет. Приложение **не загружает `.env` автоматически** (в `config.py` не задан `env_file`, зависимости `python-dotenv` нет) — переменные нужно экспортировать в окружение самому, напр. `set -a && . ./.env && set +a`. Воркер (`src/worker`) использует **собственные** классы настроек (`src/worker/notifications_config.py`, `src/worker/sync_config.py`, `src/worker/celery.py`) с частично другими префиксами (`FLOWS_DB_`, `ISSUES_DB_`, `RFI_DB_`, `RESOURCES_`, `MAILGUN_`, `WORKFLOWS_NOTIFICATIONS_` и т.д.). Источники переменных по способам запуска: | Способ запуска | Откуда берутся переменные | | --- | --- | | Локально | Переменные окружения процесса. `.env.example` — шаблон; приложение его **не** загружает автоматически, экспортируйте вручную | | Контейнер | `Dockerfile` / `Dockerfile.worker`; запуск через `entrypoint.sh` (сначала `alembic upgrade head`, затем gunicorn) | | Kubernetes (Helm, репозиторий) | `.helm/values.yaml` (`universal-chart`): блоки `envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов) для сервисов `backend`, `worker`, `scheduler` | | Kubernetes (kustomize, infra) | `iac/apps/flows/base/*.yaml`: env в `backend-deployment.yaml` / `celery-deployment.yaml`; секреты инжектируются агентом **HashiCorp Vault** (`vault.hashicorp.com/agent-inject-*`) и подгружаются в окружение перед стартом | | CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`), общий шаблон `generic/common-ci` (`universal-pipeline.yaml`) | Способы запуска процессов: | Процесс | Команда | Назначение | | --- | --- | --- | | HTTP API | `gunicorn ... flow.main:app` (`entrypoint.sh`) | Публичный и внутренний REST API (FastAPI) | | Celery worker | `celery -A src.worker worker` | Обработчик фоновых задач (рассылки, синхронизация) | | Celery beat (scheduler) | `celery -A src.worker beat -l INFO` | Периодические задачи (см. `beat_schedule` ниже) | | Alembic | `alembic upgrade head` (в `entrypoint.sh`) | Миграции БД при старте контейнера | Порядок запуска в контейнере (`entrypoint.sh`): миграции (`alembic upgrade head`), затем `gunicorn -w 3 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 --timeout $TIMEOUT ... flow.main:app`. Периодические задачи воркера (`src/worker/celery.py`, `beat_schedule`): | Задача | Расписание (UTC) | Назначение | | --- | --- | --- | | `sync_reviews` | `*/7` минут | Синхронизация review | | `notify_users` | пн–пт, 06:00 | Рассылка уведомлений пользователям | | `notify_admins_about_empty_steps` | пн–пт, 05:30 | Уведомление админов о пустых шагах | ## Переменные приложения (API) Дефолт `—` означает, что значение обязательно (иначе ошибка старта). ### Общие (`Settings`, без префикса) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `SERVICE_NAME` | string | `review-service` | Имя сервиса | | `SERVICE_HOST` | string | `0.0.0.0` | Адрес прослушивания (в кластере переопределяется внешним URL API) | | `SERVICE_PORT` | int | `8000` | Порт | | `PROXY_PATH_PREFIX` | string | `""` | Root path за реверс-прокси (в кластере `/flows`). Влияет на `root_path` FastAPI и на префикс админки | | `API_PREFIX` | string | `/api/v1` | Префикс публичного API | | `API_INTERNAL_PREFIX` | string | `/internal/v1` | Префикс внутреннего API | | `BASE_HOST` | string | `https://lk.sarex.io` | Базовый внешний URL (для ссылок/писем) | | `GATEWAY_URL` | string | `https://stage-api.sarex.io/gateway` | URL gateway (используется при `SYNC_RESOURCE_ID=1`) | | `RESOURCE_URL` | string | `https://stage-api.sarex.io/resources` | URL сервиса ресурсов/IAM (используется при `SYNC_RESOURCE_ID=1`) | | `REGISTRY` | string | `cr.yandex/crp3ccidau046kdj8g9q` | Реестр образов | | `JWT_AUTH_ENABLE` | bool | `True` | Включить аутентификацию по JWT. `False` — все запросы идут от дефолтного пользователя (удобно локально) | | `DEBUG` | bool | `False` | Режим отладки | | `ENABLE_MAILINGS` | bool | `True` | Включить рассылки | | `ENABLE_MAILGUN` | bool | `True` | Использовать Mailgun (иначе — SMTP) | | `ENABLE_CELERY` | bool | `True` | Включить постановку задач в Celery | | `ENABLE_EVENTS` | bool | `True` | Включить событийную шину | | `ENABLE_ANALYTICS` | bool | `False` | Отправлять данные в аналитику | | `SYNC_RESOURCE_ID` | bool | `False` | Определять `resource_id` через gateway/resources при создании review/документов | | `SMTP_HOST` | string \| null | `None` | SMTP-хост (альтернатива Mailgun) | | `SMTP_PORT` | int \| null | `None` | SMTP-порт | | `FROM_EMAIL` | string \| null | `None` | Адрес отправителя писем | ### Logger (`LoggerSettings`, префикс `LOG_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `LOG_LEVEL` | string | `INFO` | Уровень логирования (`INFO`/`DEBUG`/…); JSON-формат вывода | | `LOG_FORMAT` | string | JSON-шаблон | Формат строки лога | ### Auth | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `JWT_PUBLIC_KEY` | string | — | Публичный RSA-ключ для проверки подписи JWT. В кластере монтируется из секрета и экспортируется как `JWT_PUBLIC_KEY` перед стартом (см. `entrypoint`/Vault) | > Аутентификация выполняется в `src/flow/middleware.py` (`TokenUserMiddleware`). При наличии заголовка `identity` полезная нагрузка берётся из Zitadel-токена (`urn:zitadel:iam:user:metadata`), иначе — из основного `Authorization: Bearer `. Подпись проверяется публичным ключом. Пути `/docs/`, `/openapi.json/`, `/internal/` из проверки исключены. ### Database — основной PostgreSQL (`PostgresSettings`, префикс `PG_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `PG_HOST` | string | `""` | Хост PostgreSQL | | `PG_PORT` | string | `6432` | Порт PostgreSQL (обычно pgbouncer) | | `PG_LOGIN` | string | `""` | Пользователь БД | | `PG_PASSWORD` | string | `""` | Пароль БД | | `PG_DB` | string | `""` | Имя базы данных | > Итоговый DSN собирается свойством `PostgresSettings.url`: `postgresql://{login}:{password}@{host}:{port}/{db}`. ### Documentation PG (`DocumentationDBSettings`, префикс `DOCUMENTATION_PG_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `DOCUMENTATION_PG_HOST` | string | `""` | Хост БД документаций | | `DOCUMENTATION_PG_PORT` | string | `""` | Порт | | `DOCUMENTATION_PG_USERNAME` | string | `""` | Пользователь | | `DOCUMENTATION_PG_PASSWORD` | string | `""` | Пароль | | `DOCUMENTATION_PG_DATABASE` | string | `""` | Имя базы | ### RabbitMQ (`RabbitSettings`, префикс `RABBITMQ_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `RABBITMQ_HOST` | string | `localhost` | Хост | | `RABBITMQ_PORT` | string | `5672` | Порт | | `RABBITMQ_USERNAME` | string | `flow` | Пользователь | | `RABBITMQ_PASSWORD` | string | `flow` | Пароль | | `RABBITMQ_VHOST` | string | `api` | Виртуальный хост (в кластере `flows`/`flow_preprod`/`flow_prod`) | ### Celery (`CelerySettings`, префикс `CELERY_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `CELERY_QUEUE` | string | `flow` | Очередь задач. Брокер — из `RABBITMQ_*` | ### HTTP-клиенты внешних сервисов Каждый клиент — отдельный класс с полями `use`/`host`/`timeout` (и своим префиксом). Соединение создаётся httpx-клиентом. | Секция / префикс | Переменные | Назначение | | --- | --- | --- | | Sarex backend (Django) — `DJANGO_` | `DJANGO_USE` (`True`), `DJANGO_HOST` (`http://localhost:8000/api`), `DJANGO_TIMEOUT` (`60`), `DJANGO_TOKEN` (base64 `login:password` для Basic-auth) | Основной backend Sarex | | Documentations — `DOCUMENTATION_` | `DOCUMENTATION_USE` (`True`), `DOCUMENTATION_HOST`, `DOCUMENTATION_EXTERNAL_HOST`, `DOCUMENTATION_TIMEOUT` (`60`) | Сервис документаций (внутренний и внешний хост) | | EAV — `EAV_` | `EAV_HOST` (`http://eav-service.eav-prod`), `EAV_TIMEOUT` (`60`) | Сервис EAV (атрибуты) | | Planning / MSP — `PLANNING_` | `PLANNING_USE` (`True`), `PLANNING_HOST` (`https://api.sarex.io/api/pm/msp`), `PLANNING_TIMEOUT` (`60`) | Планирование | | Checklists — `CHECKLIST_` | `CHECKLIST_USE` (`True`), `CHECKLIST_HOST`, `CHECKLIST_TIMEOUT` (`60`) | Сервис чек-листов | | Workflows — `WORKFLOWS_` | `WORKFLOWS_USE` (`True`), `WORKFLOWS_HOST` (`https://lk.sarex.io/workflows/api/v1`), `WORKFLOWS_TIMEOUT` (`60`) | Сервис workflows | ### Event bus (`EventBusSettings`, префикс `EVENTS_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `EVENTS_HOST` | string | `ws://localhost:8000/ws` | Адрес WebSocket событийной шины | | `EVENTS_CONNECTION_TIMEOUT` | int | `5` | Таймаут подключения (сек) | | `EVENTS_COUNT_RETRIES` | int | `100` | Число попыток переподключения | ### Admin panel (`AdminPanelSettings`, префикс `ADMIN_PANEL_`) Админка (`sqladmin`) монтируется по пути `/api/admin/` (с учётом `PROXY_PATH_PREFIX`). | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `ADMIN_PANEL_SECRET_KEY` | string | `hex` | Секретный ключ сессии админки | | `ADMIN_PANEL_TOKEN_MAX_AGE` | int | `86400` | Время жизни токена (сек) | ### Sentry (`SentrySettings`, префикс `SENTRY_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `SENTRY_DSN` | string | `""` | DSN Sentry | | `SENTRY_ENVIRONMENT` | string | `production` | Окружение | | `SENTRY_TRACES_SAMPLE_RATE` | float | `1.0` | Доля трейсов | | `SENTRY_SEND_DEFAULT_PII` | bool | `True` | Отправлять PII | ### OpenTelemetry / трейсинг (`TraceSettings`, префикс `TRACING_`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `TRACING_USE` | bool | `False` | Включить трейсинг (при `True` инициализируется OTLP + middleware) | | `TRACING_HOST` | string | `localhost:4317` | Адрес OTLP-коллектора | | `TRACING_INSECURE` | bool | `False` | Подключение без TLS | | `TRACING_SERVICE_NAME` | string | `flows` | Имя сервиса в трейсах | | `TRACING_ENVIRONMENT` | string | `prod` | Окружение (`stage`/`preprod`/`prod`) | | `TRACING_MODULE` | string | `flows` | Атрибут `module` | | `TRACING_TEAM` | string | `team_proc` | Атрибут `team` | | `TRACING_COMPONENT` | string | `backend` | Атрибут `component` | ## Переменные только для воркера и scheduler Читаются классами из `src/worker/*`, а не основным приложением. ### Базы данных воркера | Секция / префикс | Переменные | Назначение | | --- | --- | --- | | Flows DB — `FLOWS_DB_` | `FLOWS_DB_HOST`, `FLOWS_DB_PORT`, `FLOWS_DB_DB`, `FLOWS_DB_USERNAME`, `FLOWS_DB_PASSWORD` | БД flows (для задач синхронизации/рассылок) | | Issues DB — `ISSUES_DB_` | `ISSUES_DB_HOST`, `ISSUES_DB_PORT`, `ISSUES_DB_DB`, `ISSUES_DB_USERNAME`, `ISSUES_DB_PASSWORD` | БД issues | | RFI DB — `RFI_DB_` | `RFI_DB_HOST`, `RFI_DB_PORT`, `RFI_DB_DB`, `RFI_DB_USERNAME`, `RFI_DB_PASSWORD` | БД RFI | Все пять полей каждой БД обязательны (без дефолтов). ### Клиенты и рассылки воркера | Секция / префикс | Переменные | Назначение | | --- | --- | --- | | Django-клиент — `DJANGO_` | `DJANGO_BASE_HOST`, `DJANGO_HOST`, `DJANGO_AUTH` (Basic-auth) | Получение пользователей/токенов | | Resources-клиент — `RESOURCES_` | `RESOURCES_HOST` | Пользователи, сгруппированные по ресурсам | | Flows-клиент — `FLOWS_` | `FLOWS_HOST` | Внутренние вызовы flows API (`switch_to_next_step`) | | Глобальные настройки рассылок — `NOTIFICATION_SETTINGS_` | `NOTIFICATION_SETTINGS_ENABLE_MAILINGS` (`True`), `NOTIFICATION_SETTINGS_USE_MAILGUN` (`True`) | Флаги рассылок | | Mailgun — `MAILGUN_` | `MAILGUN_HOST`, `MAILGUN_API_KEY`, `MAILGUN_SENT_FROM` (`hello@sarex.io`) | Отправка писем через Mailgun | | Workflows — `WORKFLOWS_` | `WORKFLOWS_HOST`, `WORKFLOWS_TIMEOUT` (`60`) | Постановка job в workflows | | Уведомления через Workflows — `WORKFLOWS_NOTIFICATIONS_` | `WORKFLOWS_NOTIFICATIONS_TAG` (`email`), `WORKFLOWS_NOTIFICATIONS_REGISTRY`, `WORKFLOWS_NOTIFICATIONS_SMTP_HOST`, `WORKFLOWS_NOTIFICATIONS_SMTP_PORT`, `WORKFLOWS_NOTIFICATIONS_FROM_EMAIL` | Параметры job-рассылки | ## Переменные инфраструктуры, сборки и деплоя Не читаются кодом приложения, но участвуют в запуске/сборке/деплое. | Переменная | Где используется | Назначение | | --- | --- | --- | | `TIMEOUT` | `entrypoint.sh` | Таймаут gunicorn-воркеров (сек), напр. `120` (stage) / `900` (prod) | | `ENABLE_METRICS` | `.helm/values.yaml` | Флаг метрик (`0`/`1`), кодом не читается | | `PIP_INDEX_URL` / `--extra-index-url` | `requirements.txt` | Приватный индекс пакетов Nexus (`fastapi-otel-tools`) | | `SERVICE_HOST` (в кластере) | `.helm/values.yaml`, kustomize | В кластере в `SERVICE_HOST` кладётся внешний URL API (`https://api.sarex.io/flows/api/v1`), переопределяя дефолт `0.0.0.0` | | `SAREX_MAILER_HOST` | `.helm/values.yaml` | Хост mailer-сервиса | ## Переменные из Helm-чарта (`.helm/values.yaml`) Чарт `universal-chart` описывает три сервиса — `backend`, `worker`, `scheduler`. Обычные значения задаются в блоке `envs` (с ключами по окружениям `_default`/`stage`/`preprod`/`production`), значения из секретов — в блоке `secretEnvs` (монтируются как env через `secretKeyRef`). Значения из секретов (backend): | Переменная | Секрет (`secretName`) | Ключ (`secretKey`) | | --- | --- | --- | | `PG_DB` | `postgres-secret` / `flows-postgresql-secret` | `database` | | `PG_LOGIN` | `postgres-secret` / `flows-postgresql-secret` | `username` | | `PG_PASSWORD` | `postgres-secret` / `flows-postgresql-secret` | `password` | | `PG_HOST` | `postgres-secret` / `flows-postgresql-secret` | `host` | | `SENTRY_DSN` | `sentry-secret` | `dsn` | | `SENTRY_ENVIRONMENT` | `sentry-secret` | `env` | | `DJANGO_TOKEN` | `django-secret` | `token` | | `RABBITMQ_USERNAME` | `rabbitmq-secret` / `flows-rabbitmq-secret` | `username` | | `RABBITMQ_PASSWORD` | `rabbitmq-secret` / `flows-rabbitmq-secret` | `password` | | `ADMIN_PANEL_SECRET_KEY` | `admin-secret` | `key` | | `JWT_PUBLIC_KEY` | `jwt-secret` | `public_key` | | `DOCUMENTATION_PG_*` | `documentations-postgresql-secret` / `documentations-postgres-secret` | `database`/`host`/`port`/`username`/`password` | Воркер дополнительно получает секреты `FLOWS_DB_*`, `ISSUES_DB_*`, `RFI_DB_*` (из соответствующих postgres-секретов), `DJANGO_AUTH` (`django-secret.token`), `MAILGUN_API_KEY` (`mailgun-secret.api-key`). Чарт также монтирует CA-сертификат PostgreSQL (`pg-cert` → `/root/.postgresql/root.crt`). ## Переменные из kustomize-манифестов (`iac/apps/flows`) Инфраструктурный репозиторий разворачивает те же образы через kustomize (`base` + оверлеи `brusnika-stage`, `brusnika-prod`, `yc-k8s-test`). Секреты инжектируются агентом **HashiCorp Vault** (аннотации `vault.hashicorp.com/agent-inject-*`) и подгружаются в окружение из файлов `/vault/secrets/*` перед запуском `entrypoint.sh`: | Секрет Vault | Переменные | | --- | --- | | `secrets/data/postgresql/apps/flows` | `PG_DB`, `PG_LOGIN`, `PG_HOST`, `PG_PORT`, `PG_PASSWORD`, `DOCUMENTATION_PG_*` | | `secrets/data/rabbitmq/apps/flows` | `RABBITMQ_USERNAME`, `RABBITMQ_PASSWORD`, `RABBITMQ_VHOST`, `RABBITMQ_HOST`, `RABBITMQ_PORT` | | `secrets/data/vault/common/django_auth` | `DJANGO_TOKEN` | | `secrets/data/vault/common/rsa_keys` | `JWT_PUBLIC_KEY` (public_key) | Остальные значения (`LOG_LEVEL`, `BASE_HOST`, `DJANGO_HOST`, `DOCUMENTATION_HOST`, `EAV_HOST`, `GATEWAY_URL`, `RESOURCE_URL`, `SERVICE_HOST`, `WORKFLOWS_HOST`, `CHECKLIST_HOST`, `SMTP_HOST`/`SMTP_PORT`, `FROM_EMAIL`, `ENABLE_*`, `SYNC_RESOURCE_ID`, `TIMEOUT` и т.д.) задаются напрямую в блоке `env` deployment-манифеста оверлея. ## Переменные в CI (`.gitlab-ci.yml`) Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`) и переключает окружение по ветке/тегу через `workflow.rules`: | Условие | STAND | Namespace | CHART_VERSION | | --- | --- | --- | --- | | ветка `stage` | `stage` | `proc` | `0.0.1-stage` | | ветка `master` | `preprod` | `flows-preprod` | `0.0.1-preprod` | | тег (`CI_COMMIT_TAG`) | `production` | `flows-prod` | `0.0.1-prod` | Ключевые переменные пайплайна: `SERVICE_NAME=flows-backend`, `DOCKERFILE_PATH=Dockerfile`, `IMAGE_NAME_WORKER` (образ воркера, собирается job-ом `build_worker` из `Dockerfile.worker`), `HELM_SET_ARGS` (проброс образов backend/worker/scheduler и метаданных коммита в чарт). ## Замечания и потенциальные проблемы - Приложение **не загружает `.env` автоматически** — переменные нужно экспортировать в окружение (см. `.env.example`). - У `Settings` нет `env_nested_delimiter`, поэтому вложенные секции конфигурируются **плоскими** переменными со своими префиксами (`PG_`, `DJANGO_`, `RABBITMQ_`, …), а не через `__`. - Поле `service_host` (дефолт `0.0.0.0`) и переменная `SERVICE_HOST` совпадают по имени: в кластере в `SERVICE_HOST` кладётся внешний URL API, что переопределяет адрес прослушивания в объекте настроек. Реальный адрес/порт прослушивания при запуске в контейнере задаёт gunicorn (`-b 0.0.0.0:8000` в `entrypoint.sh`), а не поле настроек. - Почта: при `ENABLE_MAILGUN=1` используется Mailgun (`MAILGUN_*` — в основном на стороне воркера), иначе — SMTP (`SMTP_HOST`/`SMTP_PORT`/`FROM_EMAIL`). - Healthcheck-эндпоинта у сервиса нет; в чарте probes (`liveness`/`readiness`) отключены. - Воркер использует отдельные классы настроек (pydantic v1 стиль `class Config`), у которых поля БД **обязательны** — при запуске воркера без `FLOWS_DB_*`/`ISSUES_DB_*`/`RFI_DB_*` будет ошибка. ## Минимальный набор для локального запуска Минимально необходимо задать: - `SERVICE_PORT` (по умолчанию `8000`), `PROXY_PATH_PREFIX` (пусто локально) - `JWT_AUTH_ENABLE=False` (чтобы не требовался `JWT_PUBLIC_KEY`) — иначе задайте `JWT_PUBLIC_KEY` - `PG_HOST`, `PG_PORT`, `PG_LOGIN`, `PG_PASSWORD`, `PG_DB` - `RABBITMQ_HOST`, `RABBITMQ_PORT`, `RABBITMQ_USERNAME`, `RABBITMQ_PASSWORD`, `RABBITMQ_VHOST` (если `ENABLE_CELERY=1`/`ENABLE_EVENTS=1`) - хосты внешних сервисов, которые реально используются: `DJANGO_HOST`, `DOCUMENTATION_HOST`, `EAV_HOST`, `CHECKLIST_HOST`, `WORKFLOWS_HOST`, `PLANNING_HOST` - при `SYNC_RESOURCE_ID=1` — `GATEWAY_URL`, `RESOURCE_URL` - `TRACING_USE=False` (иначе — `TRACING_HOST`, `TRACING_SERVICE_NAME`) - для воркера — `FLOWS_DB_*`, `ISSUES_DB_*`, `RFI_DB_*`, `MAILGUN_*` или SMTP Готовые значения-примеры для всех переменных приведены в `.env.example`.