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

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

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

Сервис — это Django-приложение (Django 5.1 + Django REST Framework), запускаемое как ASGI (`config.asgi_root:application`) через gunicorn с воркерами `uvicorn.workers.UvicornWorker`. Настройки читаются из переменных окружения через набор классов [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/), объявленных в `config/settings/base.py` и `config/settings/deps/*`.

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

- **у каждой секции свой префикс** (`env_prefix`), напр. `SERVER_`, `DB_`, `S3_`, `CACHE_`, `CLICKHOUSE_`, `KAFKA_`, `CELERY_RABBITMQ_`, `CELERY_REDIS_`, `AUTH_`, `GATEWAY_`, `EAV_`, `DOCUMENTATION_`, `USERS_`, `RESOURCES_`, `TRACING_`, `SENTRY_`;
- **вложенного делимитера нет** — каждая настройка задаётся плоской переменной вида `<PREFIX><FIELD>`, напр. `DB_HOST`, `CELERY_RABBITMQ_VHOST`;
- **`extra='ignore'`** — все классы игнорируют посторонние переменные, поэтому один общий `.env` без ошибок разбирается всеми секциями;
- **`env_file='.env'`** — в отличие от эталонного сервиса, здесь `.env` **загружается автоматически** каждым классом настроек (у `SentrySettings` — `.env.base` и `.env`). Значения из реального окружения процесса имеют приоритет над файлом.

Отдельного конфиг-файла (yaml/toml) у приложения нет. `DJANGO_SETTINGS_MODULE` по умолчанию — `config.settings.base` (см. `manage.py`).

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

| Способ запуска | Откуда берутся переменные |
| --- | --- |
| Локально (manage.py / gunicorn) | Переменные окружения процесса + файл `.env` в корне репозитория (загружается pydantic-settings) |
| Локально (docker-compose) | `docker-compose.yaml` поднимает зависимости (postgres, redis, rabbit, clickhouse, minio, pgadmin); переменные приложения задаются через окружение/`.env` |
| Kubernetes (Helm) | `.helm/values.yaml`: блок `envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов) для сервисов `api` и `celery`; чарт-зависимость `universal-chart` |
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`) и job-ы `linter`/`typechecker`/`linter_src` |

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

| Процесс | Точка входа | Назначение |
| --- | --- | --- |
| HTTP API | `docker/entrypoint.sh` → `gunicorn config.asgi_root:application` (uvicorn worker, порт 8000) | REST API |
| Celery worker/beat | `celery -A config worker -B -Q pm …` (см. `.helm/values.yaml`, сервис `celery`) | Фоновые задачи и периодические (beat) |
| `manage.py migrate` | `manage.py` | Миграции БД |
| `manage.py clean_db` / `import_data` | `manage.py` | Служебные команды (см. `README.md`) |

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

Ниже перечислены все секции настроек с их префиксами. Дефолт `—` означает отсутствие значения по умолчанию в коде.

### Server (`SERVER_*`)

Класс `ServerSettings` (`config/settings/base.py`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SERVER_HOST` | string | `https://lk.sarex.io` | Внешний базовый URL (личный кабинет); из него формируется `SERVER_MEDIA_HOST` |
| `SERVER_API_HOST` | string | `https://api.sarex.io` | Базовый URL API-шлюза (используется внешними клиентами по умолчанию) |
| `SERVER_MEDIA_ROOT` | string | `sarex/media` | Каталог медиафайлов |
| `SERVER_DEBUG` | bool | `False` | Django DEBUG. При `True` также включает `FAKE_CELERY` и обход аутентификации в `JWTAuthentication` |
| `SERVER_ENABLE_SILK` | bool | `False` | Подключить профайлер django-silk (только при `DEBUG`) |
| `SERVER_ALLOWED_HOSTS` | list[str] (JSON) | `["*"]` | Django `ALLOWED_HOSTS` |
| `SERVER_SECRET_KEY` | string | `secret` | Django `SECRET_KEY` |
| `SERVER_USE_OTEL` | bool | `False` | Включить OpenTelemetry-трейсинг и OTel-логгер (см. секцию `TRACING_*`) |
| `SERVER_VERIFY_SSL` | bool | `True` | Проверять TLS-сертификаты при обращении к внешним сервисам |
| `SERVER_LOG_LEVEL` | enum | `INFO` | `DEBUG`/`INFO`/`WARNING`/`CRITICAL`/`FATAL` |
| `SERVER_ENABLE_SYNC_RESOURCES` | bool | `False` | Включить синхронизацию ресурсов |
| `SERVER_DELETED_TASK_MAX_AGE_DAYS` | int | `30` | Срок хранения удалённых задач (дней) |
| `SERVER_EXPIRED_TASK_NOTIFICATION_HOUR` | int | `9` | Час отправки уведомлений о просроченных задачах |
| `SERVER_EXPIRED_TASK_NOTIFICATION_MAX_DAYS` | int | `7` | Горизонт уведомлений о просрочке (дней) |
| `SERVER_SEND_UPDATED_PROJECTS_INFO_INTERVAL` | int | `5` | Интервал отправки информации об обновлённых проектах |

### Auth (`AUTH_*`)

Класс `AUTHSettings` (`config/settings/deps/auth.py`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `AUTH_ALGORITHM` | string | `RS512` | Алгоритм проверки подписи JWT |
| `AUTH_PUBLIC_KEY` | string | `''` | Публичный RSA-ключ для проверки JWT в режиме sarex-backend |
| `AUTH_PUBLIC_TOKEN_URL` | string | `https://lk.sarex.io/api/token/public/` | URL получения публичного ключа/токена |

Аутентификация DRF (`REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES`) — по очереди `ZitadelJWTAuthentication`, затем `JWTAuthentication`; доступ по умолчанию `IsAuthenticated`. Zitadel-режим требует одновременно заголовки `Authorization` и `Identity`.

### Database (`DB_*`)

Класс `DBSettings` (`config/settings/deps/db.py`). PostgreSQL.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DB_ENGINE` | string | `django.db.backends.postgresql` | Движок Django ORM |
| `DB_HOST` | string | `localhost` | Хост PostgreSQL |
| `DB_PORT` | int | `5432` | Порт PostgreSQL |
| `DB_DATABASE` | string | `sarex_db` | Имя базы данных |
| `DB_USERNAME` | string | `sarex` | Пользователь БД |
| `DB_PASSWORD` | string | `sarex` | Пароль пользователя БД |

### S3 (`S3_*`)

Класс `S3Settings` (`config/settings/deps/s3.py`). Хранилище через django-storages (boto3), по умолчанию Yandex Object Storage.

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

### Cache (`CACHE_*`)

Класс `CacheSettings` (`config/settings/deps/cache.py`). Redis-кеш, включается отдельно.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CACHE_ENABLE` | bool | `False` | Включить кеш (иначе `CACHE_CLIENT=None`) |
| `CACHE_EXPIRATION` | int | `300` | TTL записей (сек) |
| `CACHE_HOST` | string | `localhost` | Хост Redis |
| `CACHE_PORT` | int | `6379` | Порт Redis |
| `CACHE_PASSWORD` | string \| null | `None` | Пароль |
| `CACHE_SSL` | bool | `False` | Подключение по TLS |
| `CACHE_SSL_CA_CERTS` | string \| null | `None` | Путь к CA-сертификату |
| `CACHE_QUEUE` | string | `default` | Имя очереди кеша |

### ClickHouse (`CLICKHOUSE_*`)

Класс `ClickHouseSettings` (`config/settings/deps/click_house.py`). Хранилище значений, включается отдельно.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CLICKHOUSE_ENABLE` | bool | `False` | Включить ClickHouse |
| `CLICKHOUSE_HOST` | string | `rc1d-…​.mdb.yandexcloud.net` | Хост |
| `CLICKHOUSE_PORT` | int | `9000` | Порт |
| `CLICKHOUSE_USER` | string | `''` | Пользователь |
| `CLICKHOUSE_PASSWORD` | string | `''` | Пароль |
| `CLICKHOUSE_DATABASE` | string | `values_db` | База данных |
| `CLICKHOUSE_TABLE` | string | `values` | Таблица |
| `CLICKHOUSE_SECURE` | bool | `False` | Защищённое подключение |
| `CLICKHOUSE_VERIFY` | bool | `False` | Проверять сертификат |
| `CLICKHOUSE_CERT` | string | `''` | Путь к CA-сертификату |

### Kafka (`KAFKA_*`)

Класс `KafkaSettings` (`config/settings/deps/kafka.py`). Продюсер сообщений, включается отдельно.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `KAFKA_ENABLE` | bool | `False` | Включить продюсер (иначе `get_producer()` вернёт `None`) |
| `KAFKA_BOOTSTRAP_SERVERS` | list[str] (JSON) | `["localhost:9092"]` | Список брокеров |
| `KAFKA_SECURITY_PROTOCOL` | string | `''` | Протокол безопасности |
| `KAFKA_SASL_MECHANISM` | string | `''` | SASL-механизм |
| `KAFKA_SASL_PLAIN_USERNAME` | string | `user` | SASL-логин |
| `KAFKA_SASL_PLAIN_PASSWORD` | string | `password` | SASL-пароль |
| `KAFKA_SSL_CAFILE` | string | `''` | Путь к CA-сертификату |
| `KAFKA_TOPICS` | dict (JSON) | `{}` | Карта топиков, напр. `{"planning": "message-hub-stage"}` |

### Celery — RabbitMQ (`CELERY_RABBITMQ_*`)

Класс `CeleryRabbitMQ` (`config/settings/deps/celery.py`). Брокер задач; из полей собирается `BROKER_URL` (`amqp://…?heartbeat=30`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CELERY_RABBITMQ_HOST` | string | `localhost` | Хост |
| `CELERY_RABBITMQ_PORT` | int | `5672` | Порт |
| `CELERY_RABBITMQ_USER` | string | `rabbit` | Пользователь |
| `CELERY_RABBITMQ_PASSWORD` | string | `rabbit` | Пароль |
| `CELERY_RABBITMQ_VHOST` | string | `api` | Виртуальный хост (в `.env`/helm — `pm`) |

### Celery — Redis (`CELERY_REDIS_*`)

Класс `CeleryRedis` (`config/settings/deps/celery.py`). Result backend; при `SSL=true` используется `rediss://` и `ssl_cert_reqs`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `CELERY_REDIS_HOST` | string | `redis` | Хост |
| `CELERY_REDIS_PORT` | int | `6379` | Порт |
| `CELERY_REDIS_DATABASE` | int | `0` | Номер БД Redis |
| `CELERY_REDIS_PASSWORD` | string \| null | `None` | Пароль (используется при SSL) |
| `CELERY_REDIS_SSL` | bool | `False` | Подключение по TLS (`rediss://`) |
| `CELERY_REDIS_SSL_CA_CERTS` | string \| null | `None` | Путь к CA-сертификату |
| `CELERY_REDIS_SSL_CERT_REQS` | string \| null | `required` | Требования к сертификату |

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

Общий базовый класс `BaseApiServiceMixin` (`config/settings/base.py`): поля `host` (по умолчанию `SERVER_API_HOST`), `api_prefix`, `internal_host`, `internal_prefix`, `timeout` (`10`), `enable` (`True`). Наследники задают собственные префиксы и дефолтные значения prefix.

| Секция / префикс | Класс | Назначение | Особенности |
| --- | --- | --- | --- |
| `GATEWAY_*` | `GateWaySetttings` | API-шлюз | `api_prefix=/gateway/api/v1` |
| `EAV_*` | `EAVSettings` | Сервис атрибутов (EAV) | `api_prefix=/eav/api/v0`, доп. `EAV_API_PREFIX_V1=/eav/api/v1` |
| `DOCUMENTATION_*` | `DocumentationSettings` | Сервис документаций | `api_prefix=/documentations/api/v1` |
| `USERS_*` | `UsersSettings` | Сервис пользователей (core) | `host=SERVER_HOST`, `api_prefix=/api/core`, `internal_host=http://localhost:8001`, `internal_prefix=/internal` |
| `RESOURCES_*` | `ResourceSettings` | Сервис ресурсов (IAM) | `internal_host=http://localhost:8001`, `internal_prefix=/api/v1` |

Для каждого клиента доступны переменные `<PREFIX>HOST`, `<PREFIX>API_PREFIX`, `<PREFIX>INTERNAL_HOST`, `<PREFIX>INTERNAL_PREFIX`, `<PREFIX>TIMEOUT`, `<PREFIX>ENABLE` (плюс `EAV_API_PREFIX_V1`).

### Tracing / OpenTelemetry (`TRACING_*`)

Класс `TracingConfig` (`config/settings/base.py`). Применяется только при `SERVER_USE_OTEL=true`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `TRACING_SERVICE_NAME` | string | `pm-backend.pm-pord` | Имя сервиса в трейсах |
| `TRACING_ENDPOINT` | string | `localhost:4317` | Адрес OTLP-коллектора |
| `TRACING_INSECURE` | bool | `False` | Небезопасное (без TLS) подключение |
| `TRACING_ENVIRONMENT` | string | `prod` | Окружение (атрибут трейса) |
| `TRACING_MODULE` | string | `planning` | Модуль (атрибут трейса) |
| `TRACING_TEAM` | string | `team_planning` | Команда (атрибут трейса) |
| `TRACING_COMPONENT` | string | `backend` | Компонент (атрибут трейса) |

### Sentry (`SENTRY_*`)

Класс `SentrySettings` (`config/settings/deps/sentry.py`). Читает `.env.base` и `.env`. Инициализируется при `SENTRY_USE=true`.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `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` | Доля профилей |

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

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

| Переменная | Где используется | Назначение |
| --- | --- | --- |
| `GUNICORN_WORKERS` | `docker/entrypoint.sh` | Число воркеров gunicorn (по умолчанию `4`) |
| `TIMEOUT` | `docker/entrypoint.sh` | Таймаут воркера gunicorn (по умолчанию `60`) |
| `NEXUS_USERNAME`, `NEXUS_PASSWORD` | `docker/Dockerfile` (build-arg), `.gitlab-ci.yml` | Доступ к приватному индексу пакетов Nexus |
| `SETTINGS_BASE_HOST` | `.helm/values.yaml` (env) | Базовый хост окружения (`stage`/`preprod`/`lk`) |

Порядок запуска контейнера (`docker/entrypoint.sh`): миграции закомментированы, сразу стартует gunicorn с ASGI-приложением `config.asgi_root:application`.

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

Чарт зависит от `universal-chart` (`oci://cr.yandex/crp3ccidau046kdj8g9q/charts`). Значения задаются для двух сервисов — `api` и `celery` — с ключами по окружениям `_default`/`stage`/`preprod`/`production`.

Обычные значения (блок `envs`) включают: `USERS_INTERNAL_HOST`, `CELERY_REDIS_HOST`, `RESOURCES_INTERNAL_HOST`, `EAV_HOST`, `EAV_API_PREFIX`, `EAV_API_PREFIX_V1`, `TRACING_ENDPOINT`, `TRACING_INSECURE`, `SERVER_ENABLE_SYNC_RESOURCES`, `SERVER_DELETED_TASK_MAX_AGE_DAYS`, `SERVER_EXPIRED_TASK_NOTIFICATION_HOUR`, `SETTINGS_BASE_HOST` (различаются адресами сервисов по окружениям).

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

| Секрет (`secretName`) | Переменные |
| --- | --- |
| `ya-pg-secret-pm` | `DB_USERNAME`, `DB_PASSWORD`, `DB_DATABASE`, `DB_HOST`, `DB_PORT` |
| `ya-s3-secret-pm` | `S3_HOST`, `S3_LOGIN`, `S3_PASSWORD`, `S3_BUCKET` |
| `cache-secret-pm` | `CACHE_HOST`, `CACHE_PORT`, `CACHE_PASSWORD`, `CACHE_SSL`, `CACHE_SSL_CA_CERTS`, `CACHE_ENABLE` |
| `clickhouse-secret-pm` | `CLICKHOUSE_HOST`, `CLICKHOUSE_PORT`, `CLICKHOUSE_USER`, `CLICKHOUSE_PASSWORD`, `CLICKHOUSE_DATABASE`, `CLICKHOUSE_TABLE`, `CLICKHOUSE_SECURE`, `CLICKHOUSE_VERIFY`, `CLICKHOUSE_CERT`, `CLICKHOUSE_ENABLE` |
| `ya-kafka-secret-pm` | `KAFKA_ENABLE`, `KAFKA_BOOTSTRAP_SERVERS`, `KAFKA_SECURITY_PROTOCOL`, `KAFKA_SASL_MECHANISM`, `KAFKA_SASL_PLAIN_USERNAME`, `KAFKA_SASL_PLAIN_PASSWORD`, `KAFKA_SSL_CAFILE`, `KAFKA_TOPICS` |
| `rabbit-secret-pm` | `CELERY_RABBITMQ_HOST`, `CELERY_RABBITMQ_PORT`, `CELERY_RABBITMQ_USER`, `CELERY_RABBITMQ_PASSWORD`, `CELERY_RABBITMQ_VHOST` |
| `server-secret-pm` | `AUTH_PUBLIC_TOKEN_URL`, `SERVER_HOST`, `SERVER_API_HOST`, `SERVER_DEBUG`, `SERVER_ALLOWED_HOSTS`, `SERVER_VERIFY_SSL`, `SERVER_LOG_LEVEL` |

Дополнительно чарт монтирует CA-сертификат ClickHouse (configMap `ch-cert`, ключ `CA.pem`) как файл `/root/clickhouse/RootCA.crt` и `tmp-volume` в `/tmp`. Прочие значения чарта (не переменные приложения): `deployment.*` (имя, реплики, ресурсы, probes на `/api/health/`), `image.*`, `service.*`, `affinity`, `owner`.

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

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

| Условие | STAND | NAMESPACE | CHART_VERSION |
| --- | --- | --- | --- |
| ветка `stage` | `stage` | `planning` | `0.0.1-stage` |
| ветка `master` | `preprod` | `pm-preprod` | `0.0.1-preprod` |
| тег (`CI_COMMIT_TAG`) | `production` | `pm-prod` | `0.0.1-prod` |
| merge request | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |

Ключевые переменные пайплайна: `SERVICE_NAME=pm-backend`, `DOCKERFILE_PATH=docker/Dockerfile`, `RELEASE_NAME`, `CHART_NAME`, `K8S_HUSTLER_BRANCH`, `IMAGE_NAME`, `HELM_SET_ARGS` (`--set universal-chart.services.{api,celery}.image.name…` и метаданные коммита). Джобы стадии `test`: `linter` (flake8 по `sarex`), `typechecker` (mypy по `src`), `linter_src` (ruff check/format по `src`).

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

- При `SERVER_DEBUG=true` `JWTAuthentication` возвращает анонимного пользователя и **аутентификация обходится** — использовать только локально.
- Все секции читают `.env` автоматически (`env_file='.env'`), поэтому один общий `.env` в корне достаточен для локального запуска. `SentrySettings` дополнительно читает `.env.base`.
- Sentry инициализируется по умолчанию (`SENTRY_USE=true`), но при пустом `SENTRY_HOST` DSN не задан — задайте `SENTRY_USE=false` локально, чтобы отключить.
- `CELERY_RABBITMQ_VHOST` в коде по умолчанию `api`, тогда как в `.env.example`/helm используется `pm` — для корректной работы очереди значение должно совпадать с брокером.
- Переменные `CACHE_PASSWORD`/`CACHE_SSL_CA_CERTS`/`CELERY_REDIS_PASSWORD` допускают `None`; в `.env` для «пустого» значения используйте `None` или закомментируйте строку.
- Список-переменные (`SERVER_ALLOWED_HOSTS`, `KAFKA_BOOTSTRAP_SERVERS`) и dict (`KAFKA_TOPICS`) задаются в формате JSON.

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

Зависимости (postgres, redis, rabbit, clickhouse, minio) поднимаются через `docker-compose up`. Минимально необходимо задать:

- `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`
- `S3_HOST`, `S3_BUCKET`, `S3_LOGIN`, `S3_PASSWORD`, `S3_VERIFY`
- `CELERY_RABBITMQ_*` (host/port/user/password/vhost) и `CELERY_REDIS_HOST`/`CELERY_REDIS_PORT`
- `SERVER_DEBUG=true` (локально), `SERVER_ALLOWED_HOSTS`, `SERVER_LOG_LEVEL`
- `AUTH_PUBLIC_KEY` (можно пустой при `SERVER_DEBUG=true`)
- адреса внешних сервисов при необходимости: `USERS_INTERNAL_HOST`, `RESOURCES_INTERNAL_HOST`, `EAV_HOST`
- `SENTRY_USE=false`, `SERVER_USE_OTEL=false` — чтобы не подключать Sentry/OTel локально
- опциональные подсистемы по флагам: `CACHE_ENABLE`, `CLICKHOUSE_ENABLE`, `KAFKA_ENABLE` (`0` по умолчанию)

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