# Конфигурация проекта sarex-backend (Django)

Документ описывает способы конфигурирования backend-сервиса `sarex` (Django) и
основные переменные окружения. Фронтенд-приложение `sarex-frontend` (шелл на
Module Federation) конфигурируется отдельно на этапе сборки — см. раздел в конце
и `ENDPOINTS.md`.

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

Сервис — это Django-приложение (проект `config`, бизнес-логика в пакете `sarex`).
Конфигурация складывается из двух механизмов:

1. **Модуль настроек Django** выбирается переменной `DJANGO_SETTINGS_MODULE`.
   Модули лежат в `config/settings/` и наследуются друг от друга через
   `from .base import *`.
2. **Переменные окружения** читаются двумя способами:
   - `django-environ` — объект `env = environ.Env()` в `config/settings/base.py`,
     вызовы `env('NAME', default=...)`, `env.bool(...)`, `env.list(...)`,
     `env.str(...)`;
   - `pydantic-settings` — классы-наследники `BaseSettings` с `env_prefix`
     (напр. `ServerSettings` → префикс `SERVER_`), инстанцируются как синглтоны
     (`SERVERSETTINGS = ServerSettings()` и т.п.).

Приложение **не загружает `.env` автоматически** в основном конфиге
(`DJANGO_READ_DOT_ENV_FILE` закомментирован). Исключение — pydantic-классы
`SentrySettings` (читает `.env.base`, `.env`), `ZitadelSettings`, `KafkaSettings`
(читают `.env`). В остальном переменные нужно экспортировать в окружение процесса.

### Модули настроек (`config/settings/*.py`)

| Модуль | Назначение |
| --- | --- |
| `base.py` | Базовые настройки, все классы `*Settings`, INSTALLED_APPS, DRF, Celery-очереди |
| `production.py` | Продакшн: `DEBUG=False`, БД из `DJANGO_POSTGRES_*`, SimpleJWT (RS512), логирование |
| `docker.py` | Наследует `test.py`, `ALLOWED_HOSTS=["*"]`, БД на хосте `postgres` |
| `test.py` / `test_ksg.py` | Прогон тестов |
| `example.local.py` / `example.ldap.local.py` | Шаблоны для локального `local.py` (копируются вручную) |

По умолчанию `manage.py` и `config/celery.py` используют `config.settings.local`.
В кластере задаётся `DJANGO_SETTINGS_MODULE=config.settings.production`, при этом
файл `production.py` **подменяется** ConfigMap-ом `django-configmap` (монтируется в
`/opt/sarex/config/settings/production.py`) — см. раздел про деплой.

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

| Процесс | Команда | Назначение |
| --- | --- | --- |
| Web/API (uWSGI) | `uwsgi --plugin python3 --ini uwsgi.ini` | HTTP API на `0.0.0.0:8000`, модуль `config.wsgi:application` |
| Web/API (dev) | `python manage.py runserver` | Локальный запуск |
| Celery worker+beat | `celery -A config worker -B -l info -E -Q default -n default_worker.%h` | Фоновые задачи и периодические таски |
| Миграции | `python manage.py migrate` | Выполняются в `entrypoint.sh` перед стартом uWSGI |

Порядок запуска контейнера backend (`entrypoint.sh`): сначала
`opentelemetry-instrument python manage.py migrate`, затем
`opentelemetry-instrument uwsgi --plugin python3 --ini uwsgi.ini`. В кластере
перед `entrypoint.sh` секреты из Vault экспортируются в окружение (`set -a; . /vault/secrets/...`).

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

Ниже перечислены основные переменные. Дефолт `—` означает, что значение
обязательно (в `production.py` без него будет ошибка старта).

### Django core

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DJANGO_SETTINGS_MODULE` | string | `config.settings.local` | Модуль настроек Django |
| `DJANGO_DEBUG` | bool | `False` | Режим отладки |
| `DJANGO_ISOLATED` | bool | `False` | Изолированный режим (в configmap отключает Sentry) |
| `ALLOWED_HOSTS` | list/str | — (в prod из env) | Разрешённые хосты; в кластере `*` |
| `APPEND_SLASH` | bool | `True` | Автодобавление слеша в URL |
| `FZ152_COMPLIANCE` | bool | `False` | Режим соответствия 152-ФЗ |
| `PDM_SYNC` | bool | `False` | Синхронизация с PDM |
| `OBJECT_STORAGE_SYNC` | bool | `True` | Синхронизация с объектным хранилищем |
| `SECRET_KEY` | string | хардкод в `production.py` | Секретный ключ Django |
| `SIMPLE_JWT_ISSUER` | string | `django` | Issuer для JWT |
| `DISK_USAGE_ROOT` | string | `/` | Корень для расчёта занятого места |
| `USE_SSL_FOR_URL_SERIALIZATION` | bool | `True` | Использовать https при сериализации URL |
| `WEB_APP_AUTH_MODE` | string | `JWTDefault` | Режим авторизации веб-приложения |

### База данных (PostgreSQL)

Читаются в `config/settings/production.py`. В кластере приходят из Vault-секрета
`secrets/data/postgresql/apps/django` (файл `/vault/secrets/django-postgresql`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DJANGO_POSTGRES_HOST` | string | — | Хост PostgreSQL |
| `DJANGO_POSTGRES_PORTS` | string | `5432` | Порт PostgreSQL |
| `DJANGO_POSTGRES_DATABASE` | string | — | Имя базы |
| `DJANGO_POSTGRES_USER` | string | — | Пользователь |
| `DJANGO_POSTGRES_PASSWORD` | string | — | Пароль |

Движок БД — `django_prometheus.db.backends.postgresql`.

### JWT (SimpleJWT, RS512)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `JWT_PRIVATE_KEY` | string | — | Приватный RSA-ключ подписи (`\n` заменяются на переводы строк). В кластере — из Vault `rsa_keys` |
| `JWT_PUBLIC_KEY` | string | — | Публичный RSA-ключ проверки |
| `JWT_KID` | string | `None` | `kid` в заголовке токена (используется для межсервисных вызовов) |
| `DJANGO_JWT_SECRET` | string | `Froom too much love of living` | Легаси-секрет |

### Celery (`CELERY_*`)

Брокер — RabbitMQ; backend результатов — Redis (по умолчанию) или Postgres
(`CELERY_USE_POSTGRES=True`). `CELERY_RABBITMQ_*` в кластере из Vault-секрета
`secrets/data/rabbitmq/apps/django`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CELERY_USE_POSTGRES` | bool | `False` | Использовать Postgres как result backend |
| `CELERY_RABBITMQ_HOST` | string | `localhost` | Хост RabbitMQ |
| `CELERY_RABBITMQ_PORT` | int | `5672` | Порт |
| `CELERY_RABBITMQ_USER` | string | `rabbit` | Пользователь |
| `CELERY_RABBITMQ_PASSWORD` | string | `rabbit` | Пароль |
| `CELERY_RABBITMQ_VHOST` | string | `api` | Виртуальный хост |
| `CELERY_REDIS_HOST` | string | `localhost` | Хост Redis (result backend) |
| `CELERY_REDIS_PORT` | int | `6379` | Порт Redis |
| `CELERY_REDIS_DATABASE` | int | `0` | Номер БД Redis |
| `CELERY_POSTGRES_DATABASE` | string | `celery_db` | БД для result backend на Postgres |
| `CELERY_POSTGRES_USER` | string | `sarex` | Пользователь |
| `CELERY_POSTGRES_PASSWORD` | string | `sarex` | Пароль |
| `CELERY_POSTGRES_HOST` | string | `localhost` | Хост |
| `CELERY_POSTGRES_PORT` | string | `5432` | Порт |

Дополнительно из Vault-шаблона прокидываются дублирующие `DJANGO_RABBIT_HOSTNAME`,
`DJANGO_RABBIT_USER`, `DJANGO_RABBIT_PASS`, `DJANGO_RABBIT_VHOST`, а также
`DJANGO_REDIS_HOST` / `DJANGO_REDIS_PORT`.

### Кеш Redis (`CACHE_*`)

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

### S3 / объектное хранилище (`S3_*`)

`S3_*` в кластере из Vault-секрета `secrets/data/minio/apps/django`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `S3_HOST` | string | `https://storage.yandexcloud.net` | Эндпоинт S3 |
| `S3_LOGIN` | string | `""` | Access key |
| `S3_PASSWORD` | string | `""` | Secret key |
| `S3_BUCKET` | string | `sarex-media-storage` | Бакет по умолчанию |
| `S3_REGION` | string | `""` | Регион (fallback: `AWS_DEFAULT_REGION`) |
| `AWS_S3_ENDPOINT_URL` | string | `https://storage.yandexcloud.net` | Эндпоинт (легаси-переменная) |
| `S3TOOLS_LIB_PATH` | string | `/opt/sarex/lib/s3tools.so` | Путь к нативной библиотеке загрузки (Dockerfile) |
| `S3TOOLS_WORKERS` | int | `10` | Число воркеров загрузки |

### Kafka (`KAFKA_*`)

Аутентификация в кластере из Vault-секрета `secrets/data/kafka/apps/django`.
Продюсер создаётся только при `SERVER_KAFKA_ENABLED=True`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `KAFKA_BOOTSTRAP_SERVERS` | list[str] (JSON) | `["localhost:9092"]` | Список брокеров |
| `KAFKA_SECURITY_PROTOCOL` | string | `""` | Протокол безопасности |
| `KAFKA_SASL_MECHANISM` | string | `""` | SASL-механизм |
| `KAFKA_SASL_PLAIN_USERNAME` | string | `user` | Логин |
| `KAFKA_SASL_PLAIN_PASSWORD` | string | `password` | Пароль |
| `KAFKA_SSL_CAFILE` | string | `""` | Путь к CA-сертификату |
| `KAFKA_TOPICS` | dict (JSON) | `{}` | Маппинг логических имён на топики |

### Sentry (`SENTRY_*`)

Читается классом `SentrySettings` из `.env.base` / `.env`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SENTRY_USE` | bool | `True` | Включить Sentry |
| `SENTRY_HOST` | string | `""` | DSN/хост Sentry |
| `SENTRY_ENVIRONMENT` | string | `""` | Окружение |
| `SENTRY_TRACES_SAMPLE_RATE` | float | `1.0` | Доля трейсов |
| `SENTRY_PROFILES_SAMPLE_RATE` | float | `0.1` | Доля профилей |

### Флаги приложения (`SERVER_*`, класс `ServerSettings`)

Класс содержит десятки булевых флагов и параметров. Наиболее значимые (реально
задаются в манифестах):

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SERVER_HOST` | string | `https://lk.sarex.io` | Внешний хост ЛК |
| `SERVER_API_HOST` | string | `https://api.sarex.io` | Внешний хост API |
| `SERVER_ZITADEL_ENABLED` | bool | `True` | Включить Zitadel-аутентификацию |
| `SERVER_KAFKA_ENABLED` | bool | `False` | Включить Kafka-продюсер |
| `SERVER_USE_METASHAPE` | bool | `True` | Использовать Metashape |
| `SERVER_USE_CLICKHOUSE` | bool | `False` | Использовать ClickHouse |
| `SERVER_CACHE_ENABLED` | bool | `False` | Включить кеш (в configmap выставляется `True`) |
| `SERVER_USE_NOTIFICATIONS` | bool | `True` | Уведомления |
| `SERVER_TIMEOUT` | int | `60` | Таймаут по умолчанию |
| `SERVER_CHUNKED_PATH` | string | — | Путь для чанкованных загрузок |

Полный список полей — в `ServerSettings` (`config/settings/base.py`). Любое поле
переопределяется переменной `SERVER_<FIELD>` в верхнем регистре.

### Workflows / processing (`WORKFLOWS_*`, класс `WorkFlowsSettings`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `WORKFLOWS_USE` | bool | `False` | Включить интеграцию с processing |
| `WORKFLOWS_HOST` | string | `https://api.sarex.io` | Хост сервиса processing |
| `WORKFLOWS_BASE_HOST` | string | `https://lk.sarex.io` | Базовый хост |
| `WORKFLOWS_PREFIX` | string | `/internal/v1` | Префикс внутреннего API |
| `WORKFLOWS_TIMEOUT` | int | `120` | Таймаут |
| `WORKFLOWS_REGISTRY` | string | `cr.yandex/crp3ccidau046kdj8g9q` | Реестр образов задач |
| `WORKFLOWS_TAG` | string | `stable` | Тег образов |

### Внешние API-сервисы (`BaseApiServiceMixin`)

Классы `GateWaySetttings` (`GATEWAY_`), `BimV2ApiSettings` (`BIMV2_`),
`EAVSettings` (`EAV_`), `AnalyticsSettings` (`ANALYTICS_`),
`DocumentationSettings` (`DOCUMENTATION_`), `UsersSettings` (`USERS_`),
`SystemLogSettings` (`SYSTEM_LOG_`), `ResourceSettings` (`RESOURCES_`) наследуют
общий набор полей:

| Поле (переменная `<PREFIX>_<FIELD>`) | Тип | Назначение |
| --- | --- | --- |
| `HOST` | string | Внешний хост сервиса |
| `API_PREFIX` | string | Префикс публичного API |
| `INTERNAL_HOST` | string | Внутренний хост (внутрикластерный) |
| `INTERNAL_PREFIX` | string | Префикс внутреннего API |
| `TIMEOUT` | int | Таймаут запроса |
| `ENABLE` | bool | Включён ли сервис |

Реально задаваемые в манифестах: `BIMV2_INTERNAL_HOST`, `BIMV2_TIMEOUT`,
`EAV_ENABLE`. Отдельно — `GK_ENCRYPTION_KEY` (класс `GatekeeperSettings`).

### Measurements (`MEASUREMENTS_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `MEASUREMENTS_HOST` | string | `https://api.sarex.io/measurements/` | Хост сервиса измерений |
| `MEASUREMENTS_TIMEOUT` | int | `180` | Таймаут |
| `MEASUREMENTS_WINDOW_SIZE` | int | `1000` | Размер окна |

### ClickHouse (`CLICKHOUSE_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CLICKHOUSE_HOST` | string | `rc1d-...yandexcloud.net` | Хост |
| `CLICKHOUSE_PORT` | int | `9000` | Порт |
| `CLICKHOUSE_USER` / `CLICKHOUSE_PASSWORD` | string | `""` | Учётные данные |
| `CLICKHOUSE_DATABASE` | string | `values_db` | База |
| `CLICKHOUSE_TABLE` | string | `values` | Таблица |
| `CLICKHOUSE_SECURE` / `CLICKHOUSE_VERIFY` | bool | `False` | TLS и проверка сертификата |
| `CLICKHOUSE_CERT` | string | `""` | CA-сертификат |

### Zitadel (`ZITADEL_*`) и Keycloak (`KC_*`, `KC_SYNC*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `ZITADEL_HOST` | string | `""` | Хост Zitadel (IdP) |
| `ZITADEL_ACCESS_TOKEN` | string | `""` | Сервисный токен (из Vault `django_auth`) |
| `ZITADEL_USERS_ENDPOINT` | string | `/v2/users` | Эндпоинт пользователей |
| `KC_SYNC_ENABLE` | bool | `False` | Включить синхронизацию с Keycloak |
| `KC_USE_REDIRECT_LOGOUT` | bool | `False` | Redirect при logout |
| `KC_CLIENT_ID` / `KC_CLIENT_SECRET` / `KC_DISCOVERY_URL` / `KC_REALM` | string | см. `KeyCloakSettings` | Параметры клиента Keycloak |

### Трейсинг (`TRACING_*`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `TRACING_SERVICE_NAME` | string | `backend.sarex-stage` | Имя сервиса в трейсах |
| `TRACING_ENDPOINT` | string | `localhost:4317` | OTLP-коллектор |
| `TRACING_INSECURE` | bool | `False` | Без TLS |
| `TRACING_ENVIRONMENT` | string | `prod` | Окружение |

### Comparator и прочее

| Переменная | Значение по умолчанию | Назначение |
| --- | --- | --- |
| `COMPARATOR_URL` | `https://wb.sarex.io/comparator` | URL сервиса сравнения |
| `COMPARATOR_SECTION` | `sarex-production-storage` | Секция хранилища |
| `COMPARATOR_JWT` | `default_jwt` | Токен сравнения |
| `WORKFLOWSSETTINGS_HOST` / `WORKFLOWSSETTINGS_REGISTRY` | — | Используются напрямую в configmap `production.py` |
| `PG_NODE_HOST`, `PG_API_KEY`, `PG_MONGO_HOST`, `PG_MONGO_PORT`, `PG_IMPORT_PATH` | см. `base.py` | Легаси-интеграции PG |

## Конфигурация в кластере (Kubernetes)

Манифесты приложения — в этом же каталоге (`base/`, оверлеи `brusnika-stage`,
`brusnika-prod`, `yc-k8s-test`). Секреты монтируются через **Vault Agent Injector**
(аннотации `vault.hashicorp.com/*` на Deployment `backend` и `celery`). Файлы
секретов в контейнере и их содержимое:

| Файл `/vault/secrets/...` | Секрет Vault | Переменные |
| --- | --- | --- |
| `django-postgresql` | `secrets/data/postgresql/apps/django` | `DJANGO_POSTGRES_HOST/PORTS/DATABASE/USER/PASSWORD` |
| `django-rabbitmq` | `secrets/data/rabbitmq/apps/django` | `CELERY_RABBITMQ_*`, `DJANGO_RABBIT_*` |
| `django-s3` | `secrets/data/minio/apps/django` | `AWS_S3_ENDPOINT_URL`, `S3_HOST/BUCKET/LOGIN/PASSWORD` |
| `django-kafka` | `secrets/data/kafka/apps/django` | `KAFKA_BOOTSTRAP_SERVERS/SECURITY_PROTOCOL/SASL_*` |
| `django-jwt-private` / `django-jwt-public` | `secrets/data/vault/common/rsa_keys` | `JWT_PRIVATE_KEY` / `JWT_PUBLIC_KEY` |
| `django-common` | `secrets/data/vault/common/django_auth` | `ZITADEL_ACCESS_TOKEN` |

Контейнер экспортирует эти файлы в окружение до запуска (`set -a; . /vault/secrets/...`).
Кроме того, `production.py` из ConfigMap содержит функцию `_load_env_file`, которая
подхватывает те же файлы при запуске `manage.py` через `kubectl exec` вне entrypoint.

Остальные (несекретные) переменные задаются в блоке `env` контейнеров
`backend`/`celery` (`SERVER_*`, `WORKFLOWS_*`, `BIMV2_*`, `MEASUREMENTS_*`,
`ZITADEL_HOST`, `KAFKA_TOPICS`, `EAV_ENABLE`, `PDM_SYNC`, `JWT_KID` и др.).

ConfigMap `django-configmap` подменяет `config/settings/production.py`
(смонтирован в `/opt/sarex/config/settings/production.py`) и переопределяет
`ALLOWED_HOSTS`, CORS, `DATABASES`, `SIMPLE_JWT`, `REST_FRAMEWORK`, `MIDDLEWARE`,
`KeyCloakSettings`, `SAREX_MODULES`, а также включает Sentry (если не `ISOLATED`).
ConfigMap `zitadel-configmap` содержит `config.json` с `client_id`/`host` Zitadel.
`uwsgi-configmap` монтирует `uwsgi.ini`.

## CI (`.gitlab-ci.yml`)

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

| Условие | STAND | NAMESPACE |
| --- | --- | --- |
| ветка `stage` | `stage` | `aero` |
| ветка `master` | `preprod` | (см. правила) |
| тег | `prod` | (см. правила) |

Ключевые переменные: `SERVICE_NAME=backend`, `DOCKERFILE_PATH=./Dockerfile`,
`RELEASE_NAME`, `CHART_NAME`, `CHART_VERSION`, `HELM_SET_ARGS`
(`universal-chart.services.backend.image.name…`, `…celery.image.name…`).

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

- В `production.py` `SECRET_KEY` задан хардкодом (закомментированный `env('SECRET_KEY')`).
  Для реального прод-развёртывания ключ желательно вынести в секрет.
- Основной конфиг не читает `.env` автоматически; переменные нужно экспортировать
  в окружение (в кластере это делает Vault + `set -a`). Только `SENTRY_*`,
  `ZITADEL_*`, `KAFKA_*` читаются из файлов `.env.base`/`.env` их pydantic-классами.
- `production.py` в репозитории backend и `production.py` из ConfigMap `django-configmap`
  — **разные** файлы. В кластере используется версия из ConfigMap (в ней, в частности,
  выставлено `DEBUG=True` в конце и включён `corsheaders`).
- `DATABASES['default']['ENGINE']` — `django_prometheus.db.backends.postgresql`
  (обёртка для метрик Prometheus).
- Значения `SERVER_*`-флагов у `backend` и `celery` местами различаются
  (напр. `SERVER_ZITADEL_ENABLED`, `SERVER_API_HOST`) — это ожидаемо.

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

Согласно `README.md` backend: поднять Postgres/Redis/RabbitMQ (docker-compose),
скопировать шаблон настроек `cp config/settings/example.local.py config/settings/local.py`,
применить миграции (`python manage.py migrate`) и создать суперпользователя.
Минимально требуются переменные БД (`DJANGO_POSTGRES_*` или значения в `local.py`),
брокера Celery (`CELERY_RABBITMQ_*`) и, при использовании соответствующих функций,
`S3_*`, `JWT_PRIVATE_KEY`/`JWT_PUBLIC_KEY`. Примеры значений — в `.env.example`
рядом с этим файлом.
