# Конфигурация проекта inspections-backend

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

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

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

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

- у приложения **нет единого общего префикса** — каждая секция задаётся собственным `env_prefix` в своём `SettingsConfigDict` (напр. `HTTP_APP_`, `DATABASE_`, `KAFKA_`, `OTEL_`, `JWT_AUTH_`, `NOTIFICATIONS_`, `SAREX_BACKEND_`, `EAV_`, `WORKFLOWS_`, `MOBILE_APP_`);
- две переменные верхнего уровня (`DEBUG`, `SERVICE_URL`) читаются без префикса;
- вложенности через разделитель нет — плоские имена вида `<PREFIX><FIELD>`, напр. `DATABASE_HOST` → `database.host`;
- почти у всех полей есть значения по умолчанию, поэтому отсутствие переменной обычно не приводит к ошибке старта — берётся дефолт из кода. В таблицах ниже приведены дефолты из `src/config.py`.

Отдельного конфиг-файла (yaml/toml) у приложения нет. Часовой пояс приложения зафиксирован в коде: `app_timezone = ZoneInfo("Europe/Moscow")`.

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

| Способ запуска | Откуда берутся переменные |
| --- | --- |
| Локально (uv) | `Makefile` через `SET_ENV` делает `set -a; source .env; set +a` перед запуском команд. Шаблон переменных — `.env.template` (в репозитории; `.env*` в `.gitignore`, кроме `.env.template`) |
| Docker | `docker/http/Dockerfile` и `docker/kafka/Dockerfile` фиксируют `PYTHONPATH=src` и `PICCOLO_CONF=db.config`; прикладные переменные пробрасываются рантаймом (k8s) |
| Kubernetes (Helm) | `.helm/values.yaml`: блоки `envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов) для сервисов `api` и `kafka-app` |
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`), сборка образа и деплой универсального чарта |

Способы запуска процессов:

| Команда (`Makefile`) | Точка входа | Назначение |
| --- | --- | --- |
| `make run` | `src/cmd/http/main.py` | HTTP API (uvicorn, `app.http:create_app`, `factory=True`) |
| — (kafka-consumer) | `src/cmd/kafka/main.py` | Обработчик Kafka-событий (FastStream) |
| `make migrate` | `piccolo migrations forwards all` | Применение миграций Piccolo |
| `make migrations` | `piccolo migrations new inspections --auto` | Генерация новой миграции |
| `make sync_eav` | `src/cmd/scripts/sync_eav.py` | Разовая синхронизация EAV |
| `make playground` | `piccolo playground run` | Локальный playground Piccolo (sqlite) |
| `make format` / `make format-check` | `ruff` | Форматирование/проверка кода |

Порядок запуска в контейнере HTTP (`docker/http/entrypoint.sh`): сначала выполняются миграции (`piccolo migrations forwards all`), затем стартует `src/cmd/http/main.py`. Контейнер kafka-consumer (`docker/kafka/entrypoint.sh`) запускает только `src/cmd/kafka/main.py` (миграции не применяет).

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

В столбце «Переменная» указано полное имя (префикс + поле). Дефолт `—` означает, что значения по умолчанию у поля нет.

### App (верхний уровень)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DEBUG` | bool | `True` | Режим отладки: логирование SQL-запросов Piccolo, `reload=True` у uvicorn, непродакшн-режим админки |
| `SERVICE_URL` | string | `https://stage.sarex.io` | Внешний базовый URL Sarex, используется при формировании ссылок (уведомления, экспорт) |

### HTTP App (`HTTP_APP_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `HTTP_APP_HOST` | string | `0.0.0.0` | Адрес прослушивания |
| `HTTP_APP_PORT` | int | `8000` | Порт |
| `HTTP_APP_ROOT_PATH` | string | `""` | Root path (префикс за реверс-прокси, напр. `/inspections`) |
| `HTTP_APP_WORKERS` | int | `1` | Число воркеров uvicorn |
| `HTTP_APP_ADMIN_ENABLE` | bool | `True` | Монтировать ли Piccolo-admin по пути `/admin/` |

### Database (`DATABASE_*`)

PostgreSQL через Piccolo (`PostgresEngine`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DATABASE_HOST` | string | `postgres` | Хост PostgreSQL |
| `DATABASE_PORT` | int | `5432` | Порт PostgreSQL |
| `DATABASE_NAME` | string | `postgres` | Имя базы данных |
| `DATABASE_USER` | string | `postgres` | Пользователь БД |
| `DATABASE_PASSWORD` | string | `postgres` | Пароль пользователя БД |

### Kafka (`KAFKA_*`)

Используется FastStream (`KafkaBroker`). Продюсер — в HTTP-приложении (публикация событий инспекций), консьюмер — отдельный процесс (`kafka-app`), подписан на топик EAV-ассетов.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `KAFKA_HOST` | string | `""` | Bootstrap-сервер (`bootstrap_servers=[host]`) |
| `KAFKA_USERNAME` | string | `""` | Пользователь (SASL) |
| `KAFKA_PASSWORD` | string | `""` | Пароль (SASL) |
| `KAFKA_SSL_CERT` | string | `""` | CA-сертификат как строка (`cadata`). Если задан — HTTP-приложение подключается по `SASLScram512` поверх TLS, иначе без SSL |
| `KAFKA_SSL_CAFILE` | string | `""` | Путь к CA-файлу (`cafile`). Если задан — kafka-consumer подключается по `SASLScram512` поверх TLS, иначе без SSL |
| `KAFKA_EAV_ASSETS_TOPIC` | string | `""` | Топик событий EAV-ассетов, на который подписан consumer |

> Механизм безопасности отличается между процессами: HTTP-приложение (`app/http.py`) смотрит на `KAFKA_SSL_CERT` (строка сертификата), а kafka-consumer (`app/kafka.py`) — на `KAFKA_SSL_CAFILE` (путь к файлу).

### OpenTelemetry (`OTEL_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `OTEL_ENABLE` | bool | `False` | Включить трейсинг/структурированное логирование. При `True` отключается `access_log` uvicorn |
| `OTEL_URL` | string | `http://signoz-otel-collector-external.signoz.svc.cluster.local:4317` | Адрес OTLP-коллектора |
| `OTEL_SERVICE_NAME` | string | `inspections-backend.inspections-stage` | Имя сервиса в трейсах |
| `OTEL_INSECURE` | bool | `True` | Небезопасное (без TLS) подключение к коллектору |

### Auth (`JWT_AUTH_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `JWT_AUTH_ENABLE` | bool | `False` | Подключать ли middleware `TokenUserMiddleware`. При `False` используется дефолтный пользователь из `entity/context.py` (для локальной разработки) |

> Middleware декодирует JWT **без проверки подписи** (`verify_signature: False`). При наличии заголовка `identity` полезная нагрузка берётся из него (метаданные Zitadel), иначе — из `authorization`. Роуты `/docs/`, `/openapi.json/`, `/admin/`, `/internal/` исключены из проверки.

### Notifications (`NOTIFICATIONS_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `NOTIFICATIONS_ENABLE` | bool | `False` | Включить отправку уведомлений об инспекциях |
| `NOTIFICATIONS_EMAIL_FROM` | string | `hello@sarex.io` | Адрес отправителя писем |

### Sarex backend (`SAREX_BACKEND_*`)

HTTP-клиент основного бэкенда Sarex (данные пользователей и т.п.).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SAREX_BACKEND_URL` | string | `https://stage.sarex.io` | Базовый URL |
| `SAREX_BACKEND_TIMEOUT` | int | `30` | Таймаут запроса (сек) |
| `SAREX_BACKEND_AUTH` | string (base64) | `base64(login:password)` | Basic-auth в виде base64(`login:password`) |

### EAV (`EAV_*`)

HTTP-клиент сервиса атрибутов (EAV).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `EAV_URL` | string | `https://stage-api.sarex.io/eav` | Базовый URL |
| `EAV_TIMEOUT` | int | `30` | Таймаут запроса (сек) |

### Workflows (`WORKFLOWS_*`)

HTTP-клиент сервиса процессов; используется, в т.ч. для запуска отправки email.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `WORKFLOWS_URL` | string | `http://workflows-api-service.processing-stage` | Базовый URL |
| `WORKFLOWS_TIMEOUT` | int | `30` | Таймаут запроса (сек) |
| `WORKFLOWS_EMAIL_DOCKER_IMAGE` | string | `cr.yandex/crp3ccidau046kdj8g9q/notification:email` | Docker-образ шага отправки email |

### Mobile App (`MOBILE_APP_*`)

Значения отдаются эндпоинтом `GET /api/v1/mobile-app/version/`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `MOBILE_APP_CURRENT_VERSION` | string | `""` | Текущая версия |
| `MOBILE_APP_RECOMMENDED_VERSION` | string | `""` | Рекомендуемая версия |
| `MOBILE_APP_REQUIRED_VERSION` | string | `""` | Минимально требуемая версия |

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

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

| Переменная | Где используется | Назначение |
| --- | --- | --- |
| `PYTHONPATH=src` | `.env.template`, `Dockerfile` | Корень пакета приложения |
| `PICCOLO_CONF=db.config` | `.env.template`, `Dockerfile` | Модуль конфигурации Piccolo (`APP_REGISTRY`, `DB`, `ADMIN_ASGI_APP`) |

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

Чарт — обёртка над зависимостью `universal-chart` (`oci://cr.yandex/crp3ccidau046kdj8g9q/charts`, версия `0.1.7`). Описаны два сервиса: `api` (HTTP) и `kafka-app` (consumer), у каждого свои блоки `envs` и `secretEnvs`. Значения различаются по окружениям через ключи `_default`/`stage`/`preprod`/`production`.

Обычные значения (`envs`) содержат те же переменные приложения, что и выше (различаются `SERVICE_URL`, `EAV_URL`, `WORKFLOWS_URL`, `OTEL_*`, `KAFKA_EAV_ASSETS_TOPIC`, `HTTP_APP_ROOT_PATH=/inspections`, `HTTP_APP_WORKERS=3`, `JWT_AUTH_ENABLE=true`, `NOTIFICATIONS_ENABLE` и т.п.). Отличия от дефолтов кода: в проде `DEBUG=false`, `OTEL_ENABLE=true`, `JWT_AUTH_ENABLE=true`.

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

| Переменная | Секрет (`_default` / `stage`) | Ключ (`secretKey`) |
| --- | --- | --- |
| `DATABASE_USER` | `ya-pg-secret` / `inspections-postgresql-secret` | `username` |
| `DATABASE_PORT` | `ya-pg-secret` / `inspections-postgresql-secret` | `port` |
| `DATABASE_NAME` | `ya-pg-secret` / `inspections-postgresql-secret` | `database` |
| `DATABASE_HOST` | `ya-pg-secret` / `inspections-postgresql-secret` | `host` |
| `DATABASE_PASSWORD` | `ya-pg-secret` / `inspections-postgresql-secret` | `password` |
| `KAFKA_HOST` | `yc-kafka-secret` / `inspections-kafka-secret` | `host` |
| `KAFKA_USERNAME` | `yc-kafka-secret` / `inspections-kafka-secret` | `username` |
| `KAFKA_PASSWORD` | `yc-kafka-secret` / `inspections-kafka-secret` | `password` |
| `KAFKA_SSL_CERT` | `inspections-kafka-secret` | `cert` |
| `SAREX_BACKEND_AUTH` | `sarex-backend-auth-secret` | `key` |

Помимо env, у сервиса `kafka-app` смонтирован CA-сертификат Yandex как файл `/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.crt` (секрет `yc-ch-certificate`, ключ `certificate`) — на него указывает `KAFKA_SSL_CAFILE`.

Прочие значения чарта (не переменные приложения): `deployment.*` (имя, реплики, ресурсы, probes), `image.*`, `service.*`, `imagePullSecrets`, у `kafka-app` — `command` (`python src/cmd/kafka/main.py`) и `volumes`.

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

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

| Условие | STAND | Namespace | Release / Chart |
| --- | --- | --- | --- |
| ветка `stage` | `stage` | `proc` | `inspections-backend` |
| ветка `master` | `preprod` | `inspections-preprod` | `inspections-backend` |
| тег (`CI_COMMIT_TAG`) | `production` | `inspections-prod` | `sarex-inspections` |
| `merge_request_event` | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |

Ключевые переменные пайплайна: `SERVICE_NAME=inspections-backend`, `DOCKERFILE_PATH=./docker/http/Dockerfile`, `CHART_VERSION`, `K8S_HUSTLER_BRANCH`, `HELM_SET_ARGS` (проброс `image.name`, `global.env`, метаданных коммита для сервисов `api` и `kafka-app`). Job `lint` прогоняет `ruff check` и `ruff format --check` на образе `uv 0.7.13 / python3.13`.

## Замечания

- Приложение **не загружает `.env` автоматически** — переменные экспортируются в окружение (в `Makefile` — через `set -a; source .env; set +a`, в k8s — через env-блоки чарта).
- Аутентификация в middleware декодирует JWT **без проверки подписи**; безопасность обеспечивается сетевым слоем/ingress. При `JWT_AUTH_ENABLE=false` активен захардкоженный дефолтный пользователь (`entity/context.py`), пригодный только для локальной разработки.
- Большинство полей конфигурации имеют дефолты — при отсутствии переменной сервис стартует со значением из кода. Для реального окружения значения задаются в `.helm/values.yaml`.
- Для экспорта поддерживается единственный формат — `xlsx` (`InspectionExportType`).

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

Скопировать `.env.template` в `.env`, поднять PostgreSQL и (при необходимости обработки событий) Kafka, применить миграции (`make migrate`) и запустить API (`make run`). Минимально стоит задать:

- `DEBUG`, `SERVICE_URL`
- `HTTP_APP_HOST`, `HTTP_APP_PORT`
- `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`
- `JWT_AUTH_ENABLE=false` (для дебага без токена)
- `KAFKA_*` (если нужен consumer/публикация событий)
- `SAREX_BACKEND_*`, `EAV_*`, `WORKFLOWS_*` (для интеграций)
- `OTEL_ENABLE=false` локально
