Add example .env files and configuration documentation for ams-sync, auth-flow, bim, cde, comparisons, django, document-link, flows, iam, inspections services.
This commit is contained in:
parent
cce76e48a9
commit
28478d9c86
85
apps/ams-sync/.env.example
Normal file
85
apps/ams-sync/.env.example
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# AMS-Sync — пример конфигурации (.env)
|
||||||
|
# Версия: 1.0.0
|
||||||
|
# Скопируйте в .env и заполните значения.
|
||||||
|
#
|
||||||
|
# Сервис имеет два режима запуска:
|
||||||
|
# - `run` — демон-потребитель Kafka (CMD контейнера), использует блоки
|
||||||
|
# App / Kafka / Zitadel / Logger / Healthcheck (+ Auth);
|
||||||
|
# - CLI — команды migrate / sync / diff (запускаются вручную),
|
||||||
|
# дополнительно используют блоки Auth / User server / БД sarex.
|
||||||
|
# Значения по умолчанию проставлены там, где они заданы в коде.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- Приложение (без префикса) ---
|
||||||
|
# LOCAL | STAGE | PREPROD | PRODUCTION
|
||||||
|
ENVIRONMENT=LOCAL
|
||||||
|
# Топик Kafka с событиями пользователей
|
||||||
|
AMS_SYNC_TOPIC=ams-sync
|
||||||
|
# ID организации-хоста в Zitadel (обязателен)
|
||||||
|
HOST_ORGANIZATION_ID=
|
||||||
|
# При True входящие model_created обрабатываются через import_user (с верификацией)
|
||||||
|
VERIFY_USERS=False
|
||||||
|
|
||||||
|
# --- Kafka (префикс KAFKA_) ---
|
||||||
|
# JSON-массив адресов брокеров, напр. ["host:9091"]
|
||||||
|
KAFKA_BOOTSTRAP_SERVERS=
|
||||||
|
KAFKA_SASL_PLAIN_USERNAME=
|
||||||
|
KAFKA_SASL_PLAIN_PASSWORD=
|
||||||
|
# Путь к CA-сертификату для SSL-подключения к Kafka
|
||||||
|
KAFKA_SSL_CAFILE=
|
||||||
|
# PLAINTEXT | SSL | SASL_PLAINTEXT | SASL_SSL
|
||||||
|
KAFKA_SECURITY_PROTOCOL=SASL_SSL
|
||||||
|
# PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512
|
||||||
|
KAFKA_SASL_MECHANISM=SCRAM-SHA-512
|
||||||
|
|
||||||
|
# --- Zitadel / AMS (префикс ZITADEL_) ---
|
||||||
|
# Хост Zitadel (обязателен для адаптера пользователей)
|
||||||
|
ZITADEL_HOST=https://idp.dev.stage.sarex.io
|
||||||
|
# Service-токен доступа к API Zitadel (обязателен)
|
||||||
|
ZITADEL_SERVICE_ACCESS_TOKEN=
|
||||||
|
# ВНИМАНИЕ: переменную читают сразу несколько классов с разным типом
|
||||||
|
# - адаптер пользователей ожидает int (минуты), напр. 10
|
||||||
|
# - адаптеры организаций/грантов ожидают строку, напр. "10m"
|
||||||
|
# Задавайте int, иначе адаптер пользователей упадёт на разборе.
|
||||||
|
ZITADEL_TIMEOUT=10
|
||||||
|
# Максимальный размер пачки при bulk-импорте
|
||||||
|
ZITADEL_MAX_BATCH=2000
|
||||||
|
# Эндпоинты (обычно не переопределяются)
|
||||||
|
ZITADEL_USERS_MANAGEMENT_ENDPOINT=management/v1/users
|
||||||
|
ZITADEL_USERS_ENDPOINT=v2/users
|
||||||
|
|
||||||
|
# --- Логирование (префикс LOG_) ---
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
# LOG_FORMAT=%(asctime)s [%(levelname)s]: %(message)s
|
||||||
|
|
||||||
|
# --- TCP Healthcheck (префикс HEALTHCHECK_) ---
|
||||||
|
HEALTHCHECK_HOST=0.0.0.0
|
||||||
|
HEALTHCHECK_PORT=8008
|
||||||
|
HEALTHCHECK_MAX_CONNECTIONS=10
|
||||||
|
HEALTHCHECK_REPLY=healthy
|
||||||
|
|
||||||
|
# --- Auth: sarex-backend (префикс AUTH_) ---
|
||||||
|
# Используется для получения JWT под админом (режим run и CLI)
|
||||||
|
AUTH_HOST=https://stage.sarex.io
|
||||||
|
AUTH_ADMIN_USERNAME=
|
||||||
|
AUTH_ADMIN_PASSWORD=
|
||||||
|
AUTH_AUTH_ENDPOINT=api/token/
|
||||||
|
|
||||||
|
# --- Sarex user server (префикс USER_SERVER_, только CLI) ---
|
||||||
|
# Источник метадаты пользователя для миграции/синхронизации
|
||||||
|
USER_SERVER_HOST=https://stage.sarex.io
|
||||||
|
USER_SERVER_USERS_ENDPOINT=api/core/admin/users
|
||||||
|
|
||||||
|
# --- База данных sarex-backend (префикс USER_DB_, только CLI) ---
|
||||||
|
# Источник пользователей/компаний для команд migrate/sync/diff
|
||||||
|
USER_DB_NAME=postgres
|
||||||
|
USER_DB_USER=postgres
|
||||||
|
USER_DB_PASSWORD=password
|
||||||
|
USER_DB_HOST=127.0.0.1
|
||||||
|
USER_DB_PORT=5432
|
||||||
|
# Имена таблиц (обычно не переопределяются)
|
||||||
|
USER_DB_USER_TABLE_NAME=base_baseuser
|
||||||
|
USER_DB_COMPANYUSER_TABLE_NAME=core_companyuser
|
||||||
|
USER_DB_COMPANY_TABLE_NAME=core_company
|
||||||
|
USER_DB_ITER_SIZE=10000
|
||||||
218
apps/ams-sync/CONFIGURATION.md
Normal file
218
apps/ams-sync/CONFIGURATION.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# Конфигурация проекта ams-sync
|
||||||
|
# Версия: 1.0.0
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||||||
|
|
||||||
|
## Что делает сервис
|
||||||
|
|
||||||
|
`ams-sync` — утилита синхронизации пользователей sarex-backend с системой управления доступом (AMS) на базе **Zitadel**. У сервиса два режима работы:
|
||||||
|
|
||||||
|
- **`run`** — фоновый демон-потребитель Kafka (`src/internal/app/service/sync_service.py`, точка входа `python3 cmd.py run`, это же `CMD` контейнера). Слушает топик событий пользователей и создаёт/обновляет пользователей в Zitadel. Параллельно поднимает TCP-healthcheck.
|
||||||
|
- **CLI-команды** (`migrate` / `sync` / `diff`, реализованы в `src/internal/app/console.py`) — разовые задачи миграции и синхронизации, запускаются вручную. Читают пользователей и компании напрямую из БД sarex-backend.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется библиотекой [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/). В отличие от монолитного `AppSettings`, настройки разбиты на несколько независимых классов, каждый со своим префиксом:
|
||||||
|
|
||||||
|
| Класс | Файл | Префикс |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Settings` (приложение) | `src/internal/app/service/settings.py` | *(без префикса)* |
|
||||||
|
| `KafkaSettings` | `src/internal/app/service/settings.py` | `kafka_` |
|
||||||
|
| `Settings` (console) | `src/internal/app/console.py` | *(без префикса)* |
|
||||||
|
| `Settings` (logger) | `src/internal/adapter/logger/logger.py` | `log_` |
|
||||||
|
| `Settings` (healthcheck) | `src/internal/adapter/tcphealthcheck/healthcheck.py` | `healthcheck_` |
|
||||||
|
| `Settings` (auth) | `src/internal/adapter/auth/http/adapter.py` | `auth_` |
|
||||||
|
| `Settings` (user server) | `src/internal/adapter/users/http/adapter.py` | `user_server_` |
|
||||||
|
| `Settings` (zitadel users) | `src/internal/adapter/users/zitadel/adapter.py` | `zitadel_` |
|
||||||
|
| `Settings` (organizations) | `src/internal/adapter/organizations/adapter.py` | `zitadel_` |
|
||||||
|
| `Settings` (grants) | `src/internal/adapter/grants/adapter.py` | `zitadel_` |
|
||||||
|
| `Settings` (user repo) | `src/internal/repository/users/repository.py` | `user_db_` |
|
||||||
|
| `Settings` (company repo) | `src/internal/repository/companies/repository.py` | `user_db_` |
|
||||||
|
|
||||||
|
Особенности разбора (`SettingsConfigDict`):
|
||||||
|
|
||||||
|
- у каждого класса задан `env_file=".env"`, `env_file_encoding="utf-8"` и `extra="ignore"` — при наличии файла `.env` в рабочей директории он загружается автоматически, лишние переменные игнорируются;
|
||||||
|
- вложенного разделителя нет — каждая группа настроек читается отдельным классом по своему префиксу (либо без префикса для классов приложения);
|
||||||
|
- CLI-команды принимают флаг `--env-file=` (по умолчанию `.env`), который прокидывается в конструкторы настроек как `_env_file`; режим `run` всегда читает `.env`.
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (CLI) | Файл `.env` (или указанный через `--env-file=`) в директории с `cmd.py` и/или переменные окружения процесса |
|
||||||
|
| Локально (контейнер) | `Dockerfile`: `CMD ["python3", "cmd.py", "run"]`; переменные пробрасываются в окружение контейнера |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`: блоки `universal-chart.envs` (обычные значения) и `universal-chart.secretEnvs` (значения из k8s-секретов). Базовый чарт — `universal-chart` |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна в `workflow.rules` (`RELEASE_NAME`, `STAND`, `CHART_*`, `IMAGE_NAME`, `HELM_SET_ARGS` и т.п.) |
|
||||||
|
|
||||||
|
Команды CLI (`python3 cmd.py <command>`, см. `README.md`):
|
||||||
|
|
||||||
|
| Команда | Аргументы | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `run` | — | Демон-потребитель Kafka + TCP-healthcheck (режим контейнера) |
|
||||||
|
| `migrate` | `--user-id`, `--with-password/--no-with-password` | Миграция пользователя(ей) из БД sarex-backend в Zitadel. Без `--user-id` мигрируются все (bulk) |
|
||||||
|
| `sync` | `--user-id`, `--company-id` | Синхронизация метадаты пользователя(ей) из БД sarex-backend в Zitadel |
|
||||||
|
| `diff` | — | Сравнение множества пользователей БД и Zitadel, отчёт по отсутствующим |
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что значение обязательно (иначе ошибка старта).
|
||||||
|
|
||||||
|
### App (без префикса)
|
||||||
|
|
||||||
|
Класс `Settings` в `src/internal/app/service/settings.py` (режим `run`) и `Settings` в `console.py` (CLI).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENVIRONMENT` | enum | `LOCAL` | Окружение: `LOCAL`/`STAGE`/`PREPROD`/`PRODUCTION` |
|
||||||
|
| `AMS_SYNC_TOPIC` | string | `ams-sync` | Имя топика Kafka с событиями пользователей |
|
||||||
|
| `HOST_ORGANIZATION_ID` | string | — | ID организации-хоста в Zitadel (обязателен и в `run`, и в CLI) |
|
||||||
|
| `VERIFY_USERS` | bool | `False` | При `True` события `model_created` обрабатываются через `import_user` (с верификацией e-mail), иначе через обычный `create` |
|
||||||
|
|
||||||
|
### Kafka (`KAFKA_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `KAFKA_BOOTSTRAP_SERVERS` | list[str] (JSON) | — | Адреса брокеров, напр. `["host:9091"]` |
|
||||||
|
| `KAFKA_SASL_PLAIN_USERNAME` | string | — | Логин SASL |
|
||||||
|
| `KAFKA_SASL_PLAIN_PASSWORD` | string | — | Пароль SASL |
|
||||||
|
| `KAFKA_SSL_CAFILE` | string | — | Путь к CA-сертификату для SSL |
|
||||||
|
| `KAFKA_SECURITY_PROTOCOL` | string | `SASL_SSL` | Протокол безопасности |
|
||||||
|
| `KAFKA_SASL_MECHANISM` | string | `SCRAM-SHA-512` | Механизм SASL |
|
||||||
|
|
||||||
|
### Zitadel / AMS (`ZITADEL_*`)
|
||||||
|
|
||||||
|
Префикс используют три класса (адаптеры пользователей, организаций и грантов). Общие поля:
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ZITADEL_SERVICE_ACCESS_TOKEN` | string | — | Service-токен доступа к API Zitadel (Bearer) |
|
||||||
|
| `ZITADEL_HOST` | string | — (у адаптера пользователей); `https://idp.dev.stage.sarex.io` (у адаптеров организаций/грантов) | Базовый URL Zitadel |
|
||||||
|
| `ZITADEL_TIMEOUT` | int / string | `10` (адаптер пользователей, минуты) / `10m` (организации, гранты) | Таймаут. См. замечание о конфликте типов ниже |
|
||||||
|
| `ZITADEL_MAX_BATCH` | int | `2000` | Максимум пользователей в одной пачке bulk-импорта |
|
||||||
|
| `ZITADEL_USERS_MANAGEMENT_ENDPOINT` | string | `management/v1/users` | Базовый путь management-API |
|
||||||
|
| `ZITADEL_USERS_ENDPOINT` | string | `v2/users` | Базовый путь users-API v2 |
|
||||||
|
|
||||||
|
### Логирование (`LOG_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `LOG_LEVEL` | string | `INFO` | Уровень логирования |
|
||||||
|
| `LOG_FORMAT` | string | `%(asctime)s [%(levelname)s]: %(message)s` | Формат строки лога |
|
||||||
|
|
||||||
|
Логи пишутся в stdout и в файл `latest.log` в рабочей директории.
|
||||||
|
|
||||||
|
### TCP Healthcheck (`HEALTHCHECK_*`)
|
||||||
|
|
||||||
|
Только режим `run`. Поднимает сырой TCP-сокет, отвечающий строкой-ответом на каждое подключение.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `HEALTHCHECK_HOST` | string | `0.0.0.0` | Адрес прослушивания |
|
||||||
|
| `HEALTHCHECK_PORT` | int | `8008` | Порт |
|
||||||
|
| `HEALTHCHECK_MAX_CONNECTIONS` | int | `10` | Размер очереди подключений (`listen`) |
|
||||||
|
| `HEALTHCHECK_REPLY` | string | `healthy` | Строка-ответ на подключение |
|
||||||
|
|
||||||
|
### Auth: sarex-backend (`AUTH_*`)
|
||||||
|
|
||||||
|
Получение JWT под сервисным админом (используется в обоих режимах — адаптер создаётся и в `run`, и в CLI).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `AUTH_HOST` | string | — | Базовый URL sarex-backend |
|
||||||
|
| `AUTH_ADMIN_USERNAME` | string | — | Логин админа |
|
||||||
|
| `AUTH_ADMIN_PASSWORD` | string | — | Пароль админа |
|
||||||
|
| `AUTH_AUTH_ENDPOINT` | string | `api/token/` | Путь получения токена |
|
||||||
|
|
||||||
|
### Sarex user server (`USER_SERVER_*`) — только CLI
|
||||||
|
|
||||||
|
Источник метадаты пользователя при миграции/синхронизации.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USER_SERVER_HOST` | string | — | Базовый URL сервиса пользователей sarex-backend |
|
||||||
|
| `USER_SERVER_USERS_ENDPOINT` | string | `api/core/admin/users` | Базовый путь эндпоинта пользователей |
|
||||||
|
|
||||||
|
### База данных sarex-backend (`USER_DB_*`) — только CLI
|
||||||
|
|
||||||
|
Один префикс на два репозитория (пользователи и компании). Подключение через `psycopg2`.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USER_DB_NAME` | string | — | Имя базы данных |
|
||||||
|
| `USER_DB_USER` | string | — | Пользователь БД |
|
||||||
|
| `USER_DB_PASSWORD` | string | — | Пароль |
|
||||||
|
| `USER_DB_HOST` | string | — | Хост PostgreSQL |
|
||||||
|
| `USER_DB_PORT` | int | — | Порт PostgreSQL |
|
||||||
|
| `USER_DB_USER_TABLE_NAME` | string | `base_baseuser` | Таблица пользователей |
|
||||||
|
| `USER_DB_COMPANYUSER_TABLE_NAME` | string | `core_companyuser` | Таблица связи пользователь↔компания |
|
||||||
|
| `USER_DB_COMPANY_TABLE_NAME` | string | `core_company` | Таблица компаний |
|
||||||
|
| `USER_DB_ITER_SIZE` | int | `10000` | Размер серверного курсора при итерации |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Базовый чарт — `universal-chart`. Обычные значения (`universal-chart.envs`) задаются на окружение (`_default`/`stage`/`preprod`/`production`):
|
||||||
|
|
||||||
|
| Переменная | Значения по окружениям |
|
||||||
|
| --- | --- |
|
||||||
|
| `AMS_SYNC_TOPIC` | `ams-sync` |
|
||||||
|
| `ENVIRONMENT` | `STAGE` / `PREPROD` / `PRODUCTION` |
|
||||||
|
| `HOST_ORGANIZATION_ID` | stage `339439562105337368`, preprod `337394329179947547`, production `337555824748561429` |
|
||||||
|
| `USER_SERVER_HOST` | stage `https://stage.sarex.io`, preprod `https://preprod.sarex.io`, production `https://.lk.sarex.io` |
|
||||||
|
| `AUTH_HOST` | stage `https://stage.sarex.io`, preprod `https://preprod.sarex.io`, production `https://lk.sarex.io` |
|
||||||
|
| `KAFKA_SECURITY_PROTOCOL` | `SASL_SSL` |
|
||||||
|
| `KAFKA_SASL_MECHANISM` | `SCRAM-SHA-512` |
|
||||||
|
|
||||||
|
Значения из секретов (`universal-chart.secretEnvs`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secretName`) | Ключ (`secretKey`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `ZITADEL_HOST` | `zitadel-token-secret` | `zitadel-host` |
|
||||||
|
| `ZITADEL_SERVICE_ACCESS_TOKEN` | `zitadel-token-secret` | `zitadel-access-token` |
|
||||||
|
| `KAFKA_BOOTSTRAP_SERVERS` | `ams-kafka-secret` | `kafka-bootstrap-servers` |
|
||||||
|
| `KAFKA_SASL_PLAIN_USERNAME` | `ams-kafka-secret` | `kafka-username` |
|
||||||
|
| `KAFKA_SASL_PLAIN_PASSWORD` | `ams-kafka-secret` | `kafka-password` |
|
||||||
|
| `KAFKA_SSL_CAFILE` | `ams-kafka-secret` | `kafka-ca-file` |
|
||||||
|
| `AUTH_ADMIN_USERNAME` | `auth-secret` | `admin-username` |
|
||||||
|
| `AUTH_ADMIN_PASSWORD` | `auth-secret` | `admin-password` |
|
||||||
|
| `USER_DB_NAME` | `user-db-secret` | `user-db-name` |
|
||||||
|
| `USER_DB_USER` | `user-db-secret` | `user-db-user` |
|
||||||
|
| `USER_DB_PASSWORD` | `user-db-secret` | `user-db-password` |
|
||||||
|
| `USER_DB_HOST` | `user-db-secret` | `user-db-host` |
|
||||||
|
| `USER_DB_PORT` | `user-db-secret` | `user-db-port` |
|
||||||
|
|
||||||
|
Помимо env, чарт монтирует CA-сертификат Kafka из секрета `ya-ca-secret` в `/etc/ca-certificates/Yandex` (`volumes`/`volumeMounts`) и раздаёт `YandexInternalRootCA.crt` через configMap `ya-ca-cert`. Прочие значения чарта (не переменные приложения): `deployment.*` (имя, реплики, ресурсы, порт `8008`), `service.*` (ClusterIP, порт `8008`), `probes.*` (TCP-пробы на `8008`, по умолчанию выключены), `serviceAccount.*`, `imagePullSecrets.*`.
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны из `generic/common-ci` и переключает окружение по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `platform` (`STAGE_NAMESPACE`) |
|
||||||
|
| ветка `master` | `preprod` | `ams-sync-preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `ams-sync-prod` |
|
||||||
|
|
||||||
|
Ключевые переменные пайплайна: `RELEASE_NAME=ams-sync`, `CHART_NAME=ams-sync`, `CHART_VERSION` (`0.0.1-<stand>`), `IMAGE_NAME`, `IMAGE_PATH=universal-chart.image.name`, `HELM_SET_ARGS`, `DOCKERFILE_PATH=Dockerfile`, флаги `ENABLE_LINTER`/`ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_STATE_UPDATE`/`ENABLE_DEPLOY`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **Конфликт `ZITADEL_TIMEOUT`.** Переменную читают три класса с общим префиксом `zitadel_`, но разного типа: адаптер пользователей ожидает `int` (минуты, дефолт `10`), адаптеры организаций и грантов — `str` (дефолт `10m`). Если задать `ZITADEL_TIMEOUT=10m`, инициализация адаптера пользователей упадёт на разборе `int`. Рекомендуется задавать целое число.
|
||||||
|
- **`ZITADEL_HOST` без дефолта у адаптера пользователей.** В `users/zitadel/adapter.py` поле `host` обязательно (дефолта нет), тогда как в адаптерах организаций/грантов есть дефолт `https://idp.dev.stage.sarex.io`. Для режима `run` переменную нужно задать явно (в Helm приходит из секрета `zitadel-token-secret`).
|
||||||
|
- **Режим `run` не использует БД и user-server.** Демон-потребитель (`sync_service.py`) поднимает только logger, healthcheck, auth-провайдер, адаптер Zitadel и Kafka. Переменные `USER_DB_*` и `USER_SERVER_*` требуются только CLI-командам (`migrate`/`sync`/`diff`), хотя в Helm они проброшены для запуска этих команд через `kubectl exec`.
|
||||||
|
- **Приложение загружает `.env` автоматически** (у всех классов задан `env_file`), в отличие от некоторых других сервисов. CLI дополнительно поддерживает `--env-file=` для альтернативного файла.
|
||||||
|
- **TLS-проверка отключена.** Все исходящие HTTP-запросы (Zitadel, sarex-backend) выполняются с `verify=False`. Отдельного флага в конфиге нет.
|
||||||
|
- **Zitadel org-ID захардкожены в коде.** Помимо `HOST_ORGANIZATION_ID`, в use-case зашиты `glorax_org_id` и `dogma_org_id`, в которые распределяются пользователи по доменам e-mail (см. `usecase/migration/usecase.py`, `usecase/user/usecase.py`).
|
||||||
|
|
||||||
|
## Минимальный набор для режима `run` (демон)
|
||||||
|
|
||||||
|
- `ENVIRONMENT`, `HOST_ORGANIZATION_ID`, `AMS_SYNC_TOPIC`
|
||||||
|
- `KAFKA_BOOTSTRAP_SERVERS`, `KAFKA_SASL_PLAIN_USERNAME`, `KAFKA_SASL_PLAIN_PASSWORD`, `KAFKA_SSL_CAFILE` (+ при необходимости `KAFKA_SECURITY_PROTOCOL`, `KAFKA_SASL_MECHANISM`)
|
||||||
|
- `ZITADEL_HOST`, `ZITADEL_SERVICE_ACCESS_TOKEN`
|
||||||
|
- `AUTH_HOST`, `AUTH_ADMIN_USERNAME`, `AUTH_ADMIN_PASSWORD`
|
||||||
|
|
||||||
|
## Дополнительно для CLI (`migrate` / `sync` / `diff`)
|
||||||
|
|
||||||
|
- `USER_SERVER_HOST`
|
||||||
|
- `USER_DB_NAME`, `USER_DB_USER`, `USER_DB_PASSWORD`, `USER_DB_HOST`, `USER_DB_PORT`
|
||||||
99
apps/ams-sync/ENDPOINTS.md
Normal file
99
apps/ams-sync/ENDPOINTS.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Интерфейсы сервиса ams-sync
|
||||||
|
# Версия: 1.0.0
|
||||||
|
|
||||||
|
Документ описывает интерфейсную поверхность сервиса: потребляемый топик Kafka, входящий TCP-healthcheck, исходящие HTTP-запросы к внешним сервисам (Zitadel, sarex-backend) и обращения к БД.
|
||||||
|
|
||||||
|
> `ams-sync` — не backend-сервис с REST API, а утилита синхронизации: демон-потребитель Kafka (`run`) плюс набор CLI-команд (`migrate`/`sync`/`diff`). Публичного HTTP API у сервиса нет — единственный входящий сокет отвечает на healthcheck сырой строкой (не HTTP). Поэтому файла `openapi.yaml` для сервиса нет.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Приложение построено по слоям (adapter → repository → usecase → controller). Исходящие вызовы к внешним сервисам выполняются через `requests` (`requests.Session` с ретраями на `5xx`), к БД — через `psycopg2`. Все HTTP-запросы идут с `verify=False` (без проверки TLS). Базовые хосты берутся из переменных окружения (см. `CONFIGURATION.md`): `ZITADEL_HOST`, `AUTH_HOST`, `USER_SERVER_HOST`.
|
||||||
|
|
||||||
|
Аутентификация исходящих запросов:
|
||||||
|
|
||||||
|
- **Zitadel** — заголовок `Authorization: Bearer <ZITADEL_SERVICE_ACCESS_TOKEN>` (service-токен из конфига). Для части операций дополнительно проставляется `x-zitadel-orgid`.
|
||||||
|
- **sarex-backend** — JWT, получаемый под админом (`AUTH_ADMIN_USERNAME`/`AUTH_ADMIN_PASSWORD`) через `POST api/token/`; кешируется и обновляется за 30 секунд до `exp`.
|
||||||
|
|
||||||
|
## Входящие интерфейсы
|
||||||
|
|
||||||
|
### TCP Healthcheck
|
||||||
|
|
||||||
|
Только режим `run`. Сырой TCP-сокет (не HTTP): на каждое подключение отправляет строку `HEALTHCHECK_REPLY` (по умолчанию `healthy`) и закрывает соединение. Слушает `HEALTHCHECK_HOST:HEALTHCHECK_PORT` (по умолчанию `0.0.0.0:8008`). В Helm k8s-пробы настроены на TCP-порт `8008` (по умолчанию отключены).
|
||||||
|
|
||||||
|
### Kafka-потребитель
|
||||||
|
|
||||||
|
Топик — переменная `AMS_SYNC_TOPIC` (по умолчанию `ams-sync`). Параметры консьюмера (`src/internal/controller/sync_service/controller.py`):
|
||||||
|
|
||||||
|
| Параметр | Значение |
|
||||||
|
| --- | --- |
|
||||||
|
| `group_id` | `ams_sync_group` |
|
||||||
|
| `auto_offset_reset` | `earliest` |
|
||||||
|
| `enable_auto_commit` | `True` |
|
||||||
|
| десериализация | JSON (`utf-8`) |
|
||||||
|
|
||||||
|
Формат сообщения — объект с полями `type` и `body`. Диспетчеризация по `type`:
|
||||||
|
|
||||||
|
| `type` сообщения | Тело (`body`) | Обработчик | Действие |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `model_created` | `SarexUserWithPermissions` | при `VERIFY_USERS=True` — `import_user` UC, иначе `user` UC `create` | Создание пользователя в Zitadel + запись метадаты |
|
||||||
|
| `model_updated` | `Metadata` | `user` UC `update_metadata` | Обновление метадаты существующего пользователя в Zitadel |
|
||||||
|
|
||||||
|
Сообщения без `type` или `body` пропускаются с предупреждением. Ошибки обработки логируются (traceback), сообщение не переотправляется (auto-commit включён).
|
||||||
|
|
||||||
|
## Исходящие HTTP-запросы
|
||||||
|
|
||||||
|
### Zitadel (`ZITADEL_HOST`)
|
||||||
|
|
||||||
|
Адаптеры `users/zitadel/adapter.py` и `organizations/adapter.py`. Итоговый URL = `<ZITADEL_HOST>` + путь.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/v2/users/new` | Создать пользователя (human) |
|
||||||
|
| POST | `/management/v1/users/human/_import` | Импортировать пользователя (заголовок `x-zitadel-orgid`) |
|
||||||
|
| POST | `/admin/v1/import` | Массовый импорт пользователей (bulk, с таймаутом) |
|
||||||
|
| POST | `/v2/users` | Поиск пользователей (по username / email / org) |
|
||||||
|
| GET | `/management/v1/users/{id}` | Получить пользователя по id |
|
||||||
|
| GET | `/management/v1/users/{id}/metadata/{key}` | Получить метадату пользователя по ключу |
|
||||||
|
| POST | `/v2/users/{id}/metadata` | Массово задать метадату пользователя (значения в base64) |
|
||||||
|
| POST | `/v2/users/{id}/deactivate` | Деактивировать пользователя |
|
||||||
|
| DELETE | `/v2/users/{id}` | Удалить пользователя |
|
||||||
|
| POST | `/v2/organizations/_search` | Найти организацию по имени |
|
||||||
|
|
||||||
|
> Пути `/v2/...` частично захардкожены в адаптере, часть базовых путей управляется `ZITADEL_USERS_MANAGEMENT_ENDPOINT` (`management/v1/users`) и `ZITADEL_USERS_ENDPOINT` (`v2/users`). Методы грантов (`grants/adapter.py`) в текущей версии закомментированы.
|
||||||
|
|
||||||
|
### sarex-backend — авторизация (`AUTH_HOST`)
|
||||||
|
|
||||||
|
Адаптер `auth/http/adapter.py`.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{AUTH_AUTH_ENDPOINT}` (по умолчанию `api/token/`) | Получить JWT по логину/паролю админа. Возвращает поле `access` |
|
||||||
|
|
||||||
|
### sarex-backend — сервис пользователей (`USER_SERVER_HOST`), только CLI
|
||||||
|
|
||||||
|
Адаптер `users/http/adapter.py`. Сессия с ретраями на `500/502/503/504` (до 5 попыток).
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `{USER_SERVER_USERS_ENDPOINT}/{user_id}/metadata` (по умолчанию `api/core/admin/users/{user_id}/metadata`) | Получить метадату пользователя sarex для синхронизации в Zitadel |
|
||||||
|
|
||||||
|
## Обращения к БД sarex-backend (`USER_DB_*`), только CLI
|
||||||
|
|
||||||
|
Репозитории `repository/users` и `repository/companies` читают данные напрямую из PostgreSQL sarex-backend (только чтение, серверный курсор `itersize`).
|
||||||
|
|
||||||
|
| Операция | Таблицы | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Пользователь по id | `base_baseuser` | `migrate --user-id` / `sync --user-id` |
|
||||||
|
| Все пользователи (итерация) | `base_baseuser` | `migrate` / `sync` / `diff` (bulk) |
|
||||||
|
| Пользователи компании | `core_companyuser` ⋈ `base_baseuser` | `sync --company-id` (только с непустым email) |
|
||||||
|
| Компания по id / имени | `core_company` | Вспомогательные запросы |
|
||||||
|
|
||||||
|
## Внешние зависимости (инфраструктура)
|
||||||
|
|
||||||
|
| Зависимость | Режим | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Kafka | `run` | Источник событий пользователей (топик `AMS_SYNC_TOPIC`) |
|
||||||
|
| Zitadel (AMS) | `run` + CLI | Целевая система управления доступом (создание/обновление/удаление пользователей и метадаты) |
|
||||||
|
| sarex-backend (auth) | `run` + CLI | Получение JWT под админом |
|
||||||
|
| sarex-backend (user server) | CLI | Источник метадаты пользователя |
|
||||||
|
| PostgreSQL sarex-backend | CLI | Источник пользователей и компаний для миграции/синхронизации |
|
||||||
15
apps/auth-flow/.env.example
Normal file
15
apps/auth-flow/.env.example
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# auth-flow-frontend — переменные СБОРКИ (build-time), НЕ рантайм-.env.
|
||||||
|
# Приложение не читает .env: значения подставляются в код на этапе сборки
|
||||||
|
# через webpack DefinePlugin (см. webpack.config.ts) или Dockerfile --build-arg / CI BUILD_ARGS.
|
||||||
|
|
||||||
|
# Окружение сборки: local | stage | prod | preprod | contour
|
||||||
|
# local -> mode=development, isDev=true (доступны dev-роуты /login, /logout, /)
|
||||||
|
# остальные -> mode=production
|
||||||
|
ENDPOINT=local
|
||||||
|
|
||||||
|
# Токен приватного npm-реестра @sarex-team (nexus.infra.sarex.io), нужен для `npm i` (.npmrc)
|
||||||
|
NPM_NEXUS_TOKEN=
|
||||||
|
|
||||||
|
# NOTE: IS_DEV вычисляется автоматически (ENDPOINT === 'local'), задавать вручную не нужно.
|
||||||
|
# NOTE: authority и client_id OIDC (Zitadel) НЕ задаются здесь — хостовое приложение
|
||||||
|
# кладёт их в localStorage (STORAGE.AUTHORITY / STORAGE.CLIENT_ID) во время работы.
|
||||||
162
apps/auth-flow/CONFIGURATION.md
Normal file
162
apps/auth-flow/CONFIGURATION.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Конфигурация проекта auth-flow-frontend
|
||||||
|
|
||||||
|
Документ описывает способы конфигурирования микрофронтенда аутентификации `auth-flow-frontend` (React + webpack), а также все переменные сборки, деплоя и рантайма.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
В отличие от бэкенд-сервисов, приложение **не читает `.env` и не использует dotenv**. Это статический фронтенд, собираемый webpack, поэтому конфигурация распределена по трём уровням:
|
||||||
|
|
||||||
|
1. **Сборка (build-time).** Переменная `ENDPOINT` определяет окружение. Её значение подставляется в код на этапе сборки через `webpack.DefinePlugin` (`webpack.config.ts`) — заменяет обращения `process.env.ENDPOINT` и `process.env.IS_DEV` на строковые литералы. Выбор режима (`development`/`production`) и флага `isDev` выполняется в `webpack/build.config.ts`.
|
||||||
|
2. **Установка зависимостей.** Пакеты `@sarex-team/*` тянутся из приватного npm-реестра (`.npmrc` → `nexus.infra.sarex.io`). Для доступа нужен токен `NPM_NEXUS_TOKEN`.
|
||||||
|
3. **Рантайм (runtime).** Параметры OIDC-провайдера — `authority` и `client_id` — читаются из `localStorage` (`src/config/zitadel.ts`) по ключам `STORAGE.AUTHORITY` и `STORAGE.CLIENT_ID` из `@sarex-team/sdk-js`. Их задаёт хостовое приложение, а не сборка.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (dev) | `npm run dev` → `ENDPOINT=local webpack serve`. Dev-сервер на `https://localhost:9000` |
|
||||||
|
| Локально (build) | `npm run build` → `webpack --config webpack.config.ts` (по умолчанию `ENDPOINT=prod`, см. `build.config.ts`) |
|
||||||
|
| Docker | `Dockerfile`: build-args `ENDPOINT` и `NPM_NEXUS_TOKEN`; сборка dist → отдача через nginx |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml` (чарт `universal-chart`): блок `services.frontend`, `global.env` |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: `workflow.rules` (ветка/тег → `STAND`, `ENDPOINT`, `CHART_VERSION`), `BUILD_ARGS`, `HELM_SET_ARGS` |
|
||||||
|
|
||||||
|
## Переменные сборки (build-time)
|
||||||
|
|
||||||
|
Подставляются в код на этапе сборки; в рантайме это уже константы.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENDPOINT` | enum | `prod` (в `build.config.ts`, если не задана) | Окружение сборки: `local`/`stage`/`prod`/`preprod`/`contour`. Определяет `mode` (`development` для `local`, иначе `production`) и `isDev` |
|
||||||
|
| `IS_DEV` | bool | — (вычисляется) | `true`, если `ENDPOINT === 'local'`. Включает dev-роуты (`/login`, `/logout`, `/`). Задаётся автоматически через `DefinePlugin`, вручную указывать не нужно |
|
||||||
|
| `NPM_NEXUS_TOKEN` | string | — | Токен авторизации в приватном npm-реестре `@sarex-team` (`.npmrc`). Обязателен для `npm i` |
|
||||||
|
|
||||||
|
> `mode` по окружениям (`webpack/build.config.ts`): `local` → `development`, `stage`/`prod`/`preprod`/`contour` → `production`. Неизвестное значение `ENDPOINT` приводит к ошибке сборки.
|
||||||
|
|
||||||
|
## Переменные рантайма (localStorage)
|
||||||
|
|
||||||
|
Читаются в браузере во время работы приложения; задаются хостовым приложением, не сборкой.
|
||||||
|
|
||||||
|
| Ключ | Источник | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `STORAGE.AUTHORITY` | `@sarex-team/sdk-js` | URL issuer'а Zitadel (`authority` OIDC). Обязателен — при отсутствии `src/config/zitadel.ts` бросает `Error("Authority or client ID is not set")` |
|
||||||
|
| `STORAGE.CLIENT_ID` | `@sarex-team/sdk-js` | `client_id` OIDC-клиента. Обязателен (та же проверка) |
|
||||||
|
| `sarex_logout` (`LOGOUT_STORAGE_KEY`) | `src/config/logout.ts` | Состояние логаута между вкладками (`{ logoutAt, owner, reason }`). Снимается только валидным токеном в `setTokensInfo` |
|
||||||
|
| access / refresh / id токены | `storage` из `@sarex-team/sdk-js` | Устанавливаются в `setTokensInfo` (`storage.setAccessToken`/`setRefreshToken`/`setIdentityToken`) после успешного колбэка |
|
||||||
|
|
||||||
|
## Конфигурация OIDC (Zitadel)
|
||||||
|
|
||||||
|
Задаётся в `src/config/zitadel.ts` (`configZitadel: ZitadelConfig`), клиент создаётся через `createZitadelAuth`.
|
||||||
|
|
||||||
|
| Параметр | Значение | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `authority` | из `localStorage` (`STORAGE.AUTHORITY`) | Issuer Zitadel |
|
||||||
|
| `client_id` | из `localStorage` (`STORAGE.CLIENT_ID`) | Идентификатор клиента |
|
||||||
|
| `response_type` | `code` | Authorization Code Flow |
|
||||||
|
| `scope` | `openid profile email offline_access urn:zitadel:iam:user:metadata` | Запрашиваемые области |
|
||||||
|
| `redirect_uri` | `${window.location.origin}/auth/callback` | URL возврата после логина |
|
||||||
|
| `post_logout_redirect_uri` | `${window.location.origin}/login` | URL после логаута |
|
||||||
|
| `redirectMethod` | `replace` | Замена записи в истории браузера |
|
||||||
|
| `revokeTokensOnSignout` | `false` | Не отзывать токены при выходе |
|
||||||
|
| `automaticSilentRenew` | `false` | Фоновое обновление токенов выключено |
|
||||||
|
| `validateSubOnSilentRenew` | `false` | — |
|
||||||
|
| `silentRequestTimeoutInSeconds` | `100` | Таймаут silent-запроса |
|
||||||
|
|
||||||
|
## Роуты приложения
|
||||||
|
|
||||||
|
Определены в `src/config/urls.ts`, диспетчеризация в `src/App.tsx`.
|
||||||
|
|
||||||
|
| Роут | Константа | Доступность | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/auth/callback` | `CALLBACK` | всегда | Обработка OIDC-колбэка (по умолчанию для неизвестных путей) |
|
||||||
|
| `/auth/error` | `ERROR` | всегда | Страница ошибки аутентификации |
|
||||||
|
| `/login` | `LOGIN` | только dev | Dev-страница входа |
|
||||||
|
| `/logout` | `LOGOUT` | только dev | Dev-страница выхода |
|
||||||
|
| `/` | `HOME` | только dev | Dev-навигация |
|
||||||
|
|
||||||
|
> В production разрешены только `/auth/callback` и `/auth/error` (`ALLOWED_PATHS`); в dev дополнительно `/login`, `/logout`, `/` (`ALLOWED_PATHS_DEV`). Неразрешённый путь заменяется на `/auth/callback` (`history.replaceState`).
|
||||||
|
|
||||||
|
## Сборка (webpack)
|
||||||
|
|
||||||
|
`webpack.config.ts` + `webpack/build.config.ts`.
|
||||||
|
|
||||||
|
| Параметр | Значение | Примечание |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `entry` | `src/index.tsx` | Точка входа |
|
||||||
|
| `output.path` | `dist/` | |
|
||||||
|
| `output.filename` | `index.js` | |
|
||||||
|
| `output.publicPath` | `/` (dev) или `/auth/callback/` (prod) | Зависит от `isDev` |
|
||||||
|
| loader | `esbuild-loader`, target `es2015` | Для `.tsx?/.jsx?` |
|
||||||
|
| `devServer` | `https`, `port: 9000`, `hot`, `historyApiFallback`, `open` | Только dev |
|
||||||
|
| plugins | `HtmlWebpackPlugin` (`public/index.html`), `DefinePlugin` (`ENDPOINT`, `IS_DEV`) | |
|
||||||
|
|
||||||
|
Node-версия для разработки: `v20.0.0` (`.nvmrc`; в `package.json` заявлено `v20.0.0`, реальная база образа — `node:20`).
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
`Dockerfile` — двухстадийная сборка.
|
||||||
|
|
||||||
|
| Стадия | База | Действия |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| build | `cr.yandex/crp3ccidau046kdj8g9q/node:20` | `npm i` (с `NPM_NEXUS_TOKEN`), `npm run build` (с `ENDPOINT`) |
|
||||||
|
| runtime | `cr.yandex/crp3ccidau046kdj8g9q/nginx:latest` | Копирует `/app/dist/` → `/dist/`, `nginx/nginx.conf` → `/etc/nginx/nginx.conf` |
|
||||||
|
|
||||||
|
Build-args: `ENDPOINT`, `NPM_NEXUS_TOKEN`.
|
||||||
|
|
||||||
|
## Nginx
|
||||||
|
|
||||||
|
`nginx/nginx.conf` — отдача статики и healthcheck.
|
||||||
|
|
||||||
|
| Локация | Поведение |
|
||||||
|
| --- | --- |
|
||||||
|
| `= /auth/callback/index.js` | `alias /dist/index.js` |
|
||||||
|
| `^~ /auth/callback` | `try_files $uri $uri/ /index.html`; заголовки `Cache-Control: no-store,...`, кеш отключён |
|
||||||
|
| `= /ping` | `200 {"result": "ok"}` (healthcheck) |
|
||||||
|
|
||||||
|
`listen 80`, `root /dist`, логи в stdout/stderr, `gzip on`.
|
||||||
|
|
||||||
|
## Helm-чарт (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Деплой через зонтичный чарт `universal-chart` (`oci://cr.yandex/crp3ccidau046kdj8g9q/charts`, версия `0.1.7`; `Chart.yaml` приложения — `auth-flow-frontend` `1.0.0`). Значения задаются с ключами по окружениям (`_default`/`stage`/`preprod`/`production`).
|
||||||
|
|
||||||
|
| Параметр | Значение | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `global.env` | `stage` (`_default` в файле) | Активное окружение чарта |
|
||||||
|
| `services.frontend.enabled` | `true` | Включение сервиса |
|
||||||
|
| `deployment.name._default` | `auth-flow-frontend` | Имя деплоймента |
|
||||||
|
| `deployment.replicaCount` | `_default: 1`, `production: 2` | Число реплик |
|
||||||
|
| `deployment.port._default` | `80` | Порт контейнера |
|
||||||
|
| `deployment.revisionHistoryLimit._default` | `5` | Хранимые ревизии |
|
||||||
|
| `deployment.resources.requests` | `memory: 128Mi`, `cpu: 100m` | Реквесты ресурсов |
|
||||||
|
| `deployment.probes.liveness/readiness` | `httpGet` `/` : `80` | Пробы |
|
||||||
|
| `image.name._default` | `cr.yandex/crp3ccidau046kdj8g9q/auth-flow-frontend` | Образ |
|
||||||
|
| `image.pullPolicy._default` | `IfNotPresent` | |
|
||||||
|
| `imagePullSecrets.name._default` | `dockerhub` (enabled `false`) | Секрет реестра |
|
||||||
|
| `service` | `ClusterIP`, port/targetPort `80`, portName `http`, имя `auth-flow-frontend-service` | Service |
|
||||||
|
| `envs` | `[]` | Переменные окружения контейнера (пусто) |
|
||||||
|
| `volumes._default` | `[]` | Тома |
|
||||||
|
| `commitSha`/`gitlabUri`/`gitlabJobUrl`/`owner` | заполняются из CI (`owner` по умолчанию `team-abc`) | Метаданные |
|
||||||
|
|
||||||
|
## CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Подключает общие шаблоны `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml` ref `apps-business`).
|
||||||
|
|
||||||
|
Базовые переменные: `SERVICE_NAME=auth-flow-frontend`, `DOCKERFILE_PATH=./Dockerfile`, `CI_TRIGGER_SOURCE=app`.
|
||||||
|
|
||||||
|
Переключение окружения по `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | ENDPOINT | CHART_VERSION | K8S_HUSTLER_BRANCH |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `merge_request_event` | — | — | — (`ENABLE_BUILD_IMAGE=false`) | — |
|
||||||
|
| ветка `stage` | `stage` | `stage` | `0.0.1-stage` | `universal-chart-stage` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `production` | `prod` | `0.0.1-prod` | `universal-chart-production` |
|
||||||
|
|
||||||
|
> Правило для ветки `master` → `preprod` присутствует, но **закомментировано**. `NAMESPACE` для всех окружений — `platform`, `RELEASE_NAME`/`CHART_NAME` — `${SERVICE_NAME}`.
|
||||||
|
|
||||||
|
`BUILD_ARGS` прокидывают `--build-arg ENDPOINT=<env>` и `--build-arg NPM_NEXUS_TOKEN=${NPM_NEXUS_TOKEN}`. `HELM_SET_ARGS` устанавливают `universal-chart.services.frontend.image.name.<env>`, `global.env`, а также `commitSha`, `gitlabUri`, `gitlabJobUrl`, `owner`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **Обязательные runtime-параметры.** Если в `localStorage` нет `AUTHORITY` или `CLIENT_ID`, `src/config/zitadel.ts` бросает ошибку при инициализации — приложение не стартует. Эти значения должен положить хостовый сервис до загрузки микрофронтенда.
|
||||||
|
- **Нет `.env`.** Приложение не читает dotenv; `process.env.ENDPOINT`/`IS_DEV` существуют только на этапе сборки (замена через `DefinePlugin`). Значение `ENDPOINT` задаётся только через CLI/`--build-arg`/CI.
|
||||||
|
- **publicPath в prod** — `/auth/callback/`: приложение обслуживается nginx под префиксом `/auth/callback`, что согласовано с `redirect_uri` OIDC.
|
||||||
|
- **`silentRenew` отключён** намеренно (`automaticSilentRenew: false`) — обновление токенов не выполняется в фоне.
|
||||||
80
apps/auth-flow/ENDPOINTS.md
Normal file
80
apps/auth-flow/ENDPOINTS.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует auth-flow-frontend
|
||||||
|
|
||||||
|
Документ описывает внешние эндпоинты, внутренние роуты и каналы межвкладочной коммуникации микрофронтенда аутентификации `auth-flow-frontend`.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Приложение отвечает только за аутентификацию по протоколу **OIDC (Authorization Code Flow + PKCE)**. Прямых REST-запросов к бэкенд-сервисам Sarex у него **нет** — всё сетевое взаимодействие идёт с провайдером идентификации **Zitadel** через обёртку `@sarex-team/sdk-js/zitadel` (поверх `oidc-client`).
|
||||||
|
|
||||||
|
Клиент создаётся в `src/config/zitadel.ts` (`createZitadelAuth(configZitadel)`). Базовый адрес провайдера (`authority`, issuer) и `client_id` берутся из `localStorage` во время работы, а не задаются сборкой. OIDC-клиент сам находит конкретные эндпоинты issuer'а через discovery-документ `/.well-known/openid-configuration`.
|
||||||
|
|
||||||
|
## Провайдер идентификации (Zitadel) по окружениям
|
||||||
|
|
||||||
|
Фактический `authority` подставляется хостовым приложением через `localStorage` (`STORAGE.AUTHORITY`) — в самом `auth-flow-frontend` хосты не захардкожены. Ниже — известные для платформы Sarex значения (справочно):
|
||||||
|
|
||||||
|
| Окружение | `authority` (issuer) |
|
||||||
|
| --- | --- |
|
||||||
|
| `stage` | `https://idp.dev.stage.sarex.io` |
|
||||||
|
| `prod` | `https://login.sarex.io` |
|
||||||
|
|
||||||
|
## OIDC-эндпоинты провайдера
|
||||||
|
|
||||||
|
Обнаруживаются через discovery и вызываются `oidc-client` относительно `authority`. Итоговые пути определяются метаданными issuer'а.
|
||||||
|
|
||||||
|
| Эндпоинт (метаданные) | Метод | Где инициируется | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/.well-known/openid-configuration` | GET | инициализация `UserManager` | Discovery метаданных провайдера |
|
||||||
|
| `authorization_endpoint` | GET (redirect) | `LoginPage` → `zitadel.authorize()` | Старт авторизации, редирект на форму входа |
|
||||||
|
| `token_endpoint` | POST | `AuthCallbackPage` → `zitadel.userManager.signinCallback()` | Обмен `code` → access/refresh/id токены |
|
||||||
|
| `jwks_uri` | GET | `oidc-client` | Ключи для проверки подписи токенов |
|
||||||
|
| `userinfo_endpoint` | GET | `oidc-client` (при необходимости) | Профиль пользователя |
|
||||||
|
| `end_session_endpoint` | GET (redirect) | `LogoutPage` → `zitadel.signout()` | Завершение сессии (логаут) |
|
||||||
|
|
||||||
|
Параметры OIDC-запросов (из `configZitadel`):
|
||||||
|
|
||||||
|
- `response_type`: `code`
|
||||||
|
- `scope`: `openid profile email offline_access urn:zitadel:iam:user:metadata`
|
||||||
|
- `redirect_uri`: `${window.location.origin}/auth/callback`
|
||||||
|
- `post_logout_redirect_uri`: `${window.location.origin}/login`
|
||||||
|
- `revokeTokensOnSignout`: `false`, `automaticSilentRenew`: `false`
|
||||||
|
|
||||||
|
## Внутренние роуты приложения
|
||||||
|
|
||||||
|
Диспетчеризация — в `src/App.tsx` по `window.location.pathname` (роуты из `src/config/urls.ts`).
|
||||||
|
|
||||||
|
| Роут | Страница | Метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `/auth/callback` | `AuthCallbackPage` | — | Обработка OIDC-колбэка: `signinCallback()`, установка токенов, рассылка события в `login_channel`. Дефолт для неизвестных путей |
|
||||||
|
| `/auth/error` | `AuthErrorPage` | — | Отображение ошибки аутентификации (параметры `error`, `error_description`, `state`, `message` из query) |
|
||||||
|
| `/login` | `LoginPage` (dev) | — | Кнопка входа (`zitadel.authorize()`) |
|
||||||
|
| `/logout` | `LogoutPage` (dev) | — | Кнопка выхода (`zitadel.signout()`) |
|
||||||
|
| `/` | `MainPage` (dev) | — | Dev-навигация (login/logout) |
|
||||||
|
| `/ping` | nginx | GET | Healthcheck, отдаёт `{"result": "ok"}` |
|
||||||
|
|
||||||
|
## Поток аутентификации (кратко)
|
||||||
|
|
||||||
|
1. `LoginPage` вызывает `zitadel.authorize()` → редирект на `authorization_endpoint` Zitadel.
|
||||||
|
2. Провайдер возвращает пользователя на `redirect_uri` = `/auth/callback` с `code`.
|
||||||
|
3. `AuthCallbackPage` вызывает `signinCallback()` → обмен `code` на токены через `token_endpoint`.
|
||||||
|
4. Токены сохраняются (`setTokensInfo` → `storage.setAccessToken/setRefreshToken/setIdentityToken`), снимается флаг логаута.
|
||||||
|
5. Результат рассылается остальным вкладкам, происходит закрытие overlay-окна (`window.opener`) или переход на `/` (`HOME`).
|
||||||
|
6. При ошибке — редирект на `/auth/error` с деталями.
|
||||||
|
|
||||||
|
## Межвкладочная и оконная коммуникация
|
||||||
|
|
||||||
|
| Канал | Ключ / имя | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `BroadcastChannel` | `login_channel` | Сообщения `{ type: "login_success" }` и `{ type: "login_error", reason }` между вкладками |
|
||||||
|
| `localStorage` | `sarex_logout` (`LOGOUT_STORAGE_KEY`) | Состояние логаута между вкладками (`logoutAt`, `owner`, `reason`) |
|
||||||
|
| window events | `setTokensInfoIntoWindowEvents` (`@sarex-team/sdk-js`) | Оповещение хостового приложения об обновлении токенов (`reason: "auth_callback"`) |
|
||||||
|
| `window.opener` | — | Режим overlay: после успешного входа окно закрывается (`window.close()`) |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Логика — в `src/utils/authError.ts`; отображение — `AuthErrorPage`.
|
||||||
|
|
||||||
|
- Параметры ошибки читаются из query-строки колбэка: `error`, `error_description`, `state`, `message` (`parseAuthErrorFromSearch`).
|
||||||
|
- Причина ошибки: `error_description` → `message` → `error` → `"Unknown error"` (`getAuthErrorReason`).
|
||||||
|
- URL страницы ошибки собирается `buildAuthErrorUrl` (query из непустых параметров, иначе просто `/auth/error`).
|
||||||
|
- Если контекста ошибки нет — редирект на `/` (`HOME`).
|
||||||
|
- Пользователю доступны кнопки «Скопировать детали ошибки» (`formatAuthErrorForCopy`) и «Вернуться на форму входа» (`/login`), а также ссылка в поддержку `mailto:support@sarex.io`.
|
||||||
64
apps/bim/.env.example
Normal file
64
apps/bim/.env.example
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# App
|
||||||
|
# APP_NAME/APP_VERSION/LOG_LEVEL читаются кодом, но в деплой-envs их обычно не задают
|
||||||
|
APP_NAME='BIM Backend v2'
|
||||||
|
APP_VERSION=local
|
||||||
|
LOG_LEVEL=info
|
||||||
|
# Адрес прослушивания HTTP API (host:port)
|
||||||
|
API_ADDRESS=0.0.0.0:5555
|
||||||
|
|
||||||
|
# Master-кластер PostgreSQL (BIM id <= LAST_MASTER_BIM)
|
||||||
|
POSTGRES_ADDRESS=localhost
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_USER=user
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
POSTGRES_DB=bimbackendv2
|
||||||
|
POSTGRES_POOL_SIZE=10
|
||||||
|
DB_CERT_PATH=./.docker/yandex_pg.pem
|
||||||
|
|
||||||
|
# Slave-1 кластер PostgreSQL (шардирование по BIM id)
|
||||||
|
POSTGRES_ADDRESS_2=localhost
|
||||||
|
POSTGRES_PORT_2=5433
|
||||||
|
POSTGRES_USER_2=user
|
||||||
|
POSTGRES_PASSWORD_2=password
|
||||||
|
POSTGRES_DB_2=bimbackendv2
|
||||||
|
POSTGRES_POOL_SIZE_2=10
|
||||||
|
DB_CERT_PATH_2=./.docker/yandex_pg.pem
|
||||||
|
|
||||||
|
# Slave-2 кластер PostgreSQL (шардирование по BIM id)
|
||||||
|
POSTGRES_ADDRESS_3=localhost
|
||||||
|
POSTGRES_PORT_3=5434
|
||||||
|
POSTGRES_USER_3=user
|
||||||
|
POSTGRES_PASSWORD_3=password
|
||||||
|
POSTGRES_DB_3=bimbackendv2
|
||||||
|
POSTGRES_POOL_SIZE_3=10
|
||||||
|
DB_CERT_PATH_3=./.docker/yandex_pg.pem
|
||||||
|
|
||||||
|
# Границы шардирования BIM id между кластерами
|
||||||
|
LAST_MASTER_BIM=10
|
||||||
|
LAST_MASTER_BIM_V3=0
|
||||||
|
LAST_SLAVE_1_BIM=0
|
||||||
|
LAST_SLAVE_1_BIM_V3=0
|
||||||
|
|
||||||
|
# TLS до PostgreSQL (0 — локально, 1 — в облаке)
|
||||||
|
ENABLE_SSL=0
|
||||||
|
# Логировать SQL-запросы (1 — включено)
|
||||||
|
ENABLE_SQL_QUERY=1
|
||||||
|
|
||||||
|
# Внешний Django-бэкенд (проверка прав администратора)
|
||||||
|
DJANGO_HOST=https://stage.sarex.io
|
||||||
|
|
||||||
|
# Режим интеграционных тестов (в этом режиме CheckUserIsAdmin всегда true)
|
||||||
|
INTEGRATION_TESTS=0
|
||||||
|
|
||||||
|
# --- Переменные окружения, не читаемые config.go (инфраструктура/тесты/сборка) ---
|
||||||
|
# Проброс портов и локальная БД в docker-compose
|
||||||
|
# API_PORT=5555
|
||||||
|
# POSTGRES_EXTERNAL_PORT=5432
|
||||||
|
# POSTGRES_EXTERNAL_PORT_2=5433
|
||||||
|
# Значения для контейнеров тестов
|
||||||
|
# TEST_ENV=1
|
||||||
|
# TEST_BIM=1
|
||||||
|
# JWT_TEST=<jwt>
|
||||||
|
# Объявлены в .env, но config.go их не использует
|
||||||
|
# GRPC_ADDRESS=0.0.0.0
|
||||||
|
# GRPC_PORT=50051
|
||||||
189
apps/bim/CONFIGURATION.md
Normal file
189
apps/bim/CONFIGURATION.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# Конфигурация проекта bim-backend-v2
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` (функция `NewConfig`) через библиотеку [`kelseyhightower/envconfig`](https://github.com/kelseyhightower/envconfig) (структура `Config`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **Префикса нет** — `envconfig.Process("", &cfg)` вызывается с пустым префиксом, поэтому имена переменных задаются как есть (напр. `POSTGRES_ADDRESS`, `API_ADDRESS`). Имя переменной определяется тегом `envconfig:"..."` у каждого поля.
|
||||||
|
- Типы приводятся автоматически по типу поля Go (`string`, `int`, `bool`, `uint64`). Для `bool` подходят `1`/`0`/`true`/`false`.
|
||||||
|
- Ошибка разбора приводит к `panic` при старте (в `NewConfig` `envconfig.Process` завёрнут в `sync.Once`, ошибка не возвращается, а паникует).
|
||||||
|
- Отсутствующая переменная не является ошибкой — поле получает нулевое значение соответствующего типа (пустая строка, `0`, `false`). Обязательность полей не проверяется.
|
||||||
|
|
||||||
|
В отличие от python-сервисов, приложение **загружает `.env` автоматически**: в `cmd/httpserver/main.go` вызывается `godotenv.Load(".env")` (если файла нет — печатается предупреждение и используются переменные окружения процесса).
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Файл `.env` в рабочем каталоге (`godotenv.Load`) либо переменные окружения процесса |
|
||||||
|
| Локально (контейнеры) | `.docker/docker-compose.yml`: `env_file` → `.docker/.env` + `.docker/.docker.env` |
|
||||||
|
| Kubernetes (Helm, репозиторий приложения) | `.helm/values-<env>.yaml`: блоки `envs` (обычные значения) и `secrets` (из k8s-секретов); шаблон `.helm/templates/api.yaml` |
|
||||||
|
| Kubernetes (IaC, этот репозиторий) | `iac/apps/bim/base/backend-deployment.yaml`: блок `env` и секреты из Vault (аннотации `vault.hashicorp.com/*`, шаблон `bim-postgresql`) |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: подключение общих пайплайнов `generic/common-ci` и переменные `workflow.rules` по ветке/тегу |
|
||||||
|
|
||||||
|
Способы запуска процессов:
|
||||||
|
|
||||||
|
| Команда | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `httpserver` (`make api` → `go install ./cmd/httpserver`) | `cmd/httpserver/main.go` | HTTP API. При старте применяет миграции (`appmigrations.RunOnStartup`), поднимает `net/http/pprof` на `:8081`, затем запускает API на `API_ADDRESS` |
|
||||||
|
| `migrations` (`go install ./cmd/migrations`) | `cmd/migrations/main.go` | Отдельный запуск миграций БД |
|
||||||
|
|
||||||
|
Порядок запуска в контейнере (`.docker/entrypoint.sh`): запускается `/go/bin/httpserver` (строка запуска миграций закомментирована — миграции выполняются самим приложением на старте). Финальный образ (`.docker/api.dockerfile`) — `scratch` со статически слинкованным бинарником `httpserver` и вшитым CA-сертификатом Postgres (`/root/yandex_pg.pem`).
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Ниже перечислены все переменные, читаемые кодом (`config/config.go`). Префикса нет. Дефолт — это нулевое значение типа Go, если переменная не задана.
|
||||||
|
|
||||||
|
### App
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `APP_NAME` | string | `""` | Имя приложения (в коде помечено TODO — в deploy-envs не задаётся) |
|
||||||
|
| `APP_VERSION` | string | `""` | Версия приложения (TODO — в deploy-envs не задаётся) |
|
||||||
|
| `LOG_LEVEL` | string | `""` | Уровень логирования, передаётся в `logging.NewLogger` (TODO — в deploy-envs не задаётся) |
|
||||||
|
| `API_ADDRESS` | string | `""` | Адрес прослушивания HTTP API в формате `host:port` (напр. `0.0.0.0:8080`) |
|
||||||
|
| `TEST_ENV` | string | `""` | Служебное поле для тестов |
|
||||||
|
|
||||||
|
### PostgreSQL
|
||||||
|
|
||||||
|
Сервис работает с **тремя** кластерами PostgreSQL (master, slave-1, slave-2). Выбор кластера для конкретного BIM выполняется по его id в `internal/app/http/httpserver.go` (сравнение с `LAST_MASTER_BIM*`/`LAST_SLAVE_1_BIM*`). Строка подключения собирается в `Config.GetPostgresConnectionURL` (`postgres://user:password@addr:port/db`, при `ENABLE_SSL=false` добавляется `?sslmode=disable`).
|
||||||
|
|
||||||
|
Master-кластер:
|
||||||
|
|
||||||
|
| Переменная | Тип | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS` | string | Хост PostgreSQL (master) |
|
||||||
|
| `POSTGRES_PORT` | string | Порт PostgreSQL (master) |
|
||||||
|
| `POSTGRES_USER` | string | Пользователь БД |
|
||||||
|
| `POSTGRES_PASSWORD` | string | Пароль пользователя БД |
|
||||||
|
| `POSTGRES_DB` | string | Имя базы данных |
|
||||||
|
| `POSTGRES_POOL_SIZE` | int | Размер пула соединений |
|
||||||
|
| `DB_CERT_PATH` | string | Путь к CA-сертификату PostgreSQL (используется при `ENABLE_SSL=1`) |
|
||||||
|
|
||||||
|
Slave-1 кластер — те же поля с суффиксом `_2`:
|
||||||
|
|
||||||
|
| Переменная | Тип | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS_2` | string | Хост PostgreSQL (slave-1) |
|
||||||
|
| `POSTGRES_PORT_2` | string | Порт |
|
||||||
|
| `POSTGRES_USER_2` | string | Пользователь |
|
||||||
|
| `POSTGRES_PASSWORD_2` | string | Пароль |
|
||||||
|
| `POSTGRES_DB_2` | string | База данных |
|
||||||
|
| `POSTGRES_POOL_SIZE_2` | int | Размер пула |
|
||||||
|
| `DB_CERT_PATH_2` | string | Путь к CA-сертификату |
|
||||||
|
|
||||||
|
Slave-2 кластер — те же поля с суффиксом `_3`:
|
||||||
|
|
||||||
|
| Переменная | Тип | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS_3` | string | Хост PostgreSQL (slave-2) |
|
||||||
|
| `POSTGRES_PORT_3` | string | Порт |
|
||||||
|
| `POSTGRES_USER_3` | string | Пользователь |
|
||||||
|
| `POSTGRES_PASSWORD_3` | string | Пароль |
|
||||||
|
| `POSTGRES_DB_3` | string | База данных |
|
||||||
|
| `POSTGRES_POOL_SIZE_3` | int | Размер пула |
|
||||||
|
| `DB_CERT_PATH_3` | string | Путь к CA-сертификату |
|
||||||
|
|
||||||
|
### Шардирование BIM по кластерам
|
||||||
|
|
||||||
|
| Переменная | Тип | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `LAST_MASTER_BIM` | uint64 | Верхняя граница id BIM для master-кластера (API v1) |
|
||||||
|
| `LAST_MASTER_BIM_V3` | uint64 | Верхняя граница id BIM для master-кластера (API v2 / BIM v3) |
|
||||||
|
| `LAST_SLAVE_1_BIM` | uint64 | Верхняя граница id BIM для slave-1 (API v1) |
|
||||||
|
| `LAST_SLAVE_1_BIM_V3` | uint64 | Верхняя граница id BIM для slave-1 (API v2 / BIM v3) |
|
||||||
|
|
||||||
|
### Прочее
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_SSL` | bool | `false` | Подключение к PostgreSQL по TLS. При `false` в DSN добавляется `sslmode=disable` |
|
||||||
|
| `ENABLE_SQL_QUERY` | bool | `false` | Логировать SQL-запросы |
|
||||||
|
| `DJANGO_HOST` | string | `""` | Базовый URL Django-бэкенда для проверки прав администратора (см. `ENDPOINTS.md`) |
|
||||||
|
| `INTEGRATION_TESTS` | bool | `false` | Режим интеграционных тестов. При `true` `CheckUserIsAdmin` всегда возвращает `true` |
|
||||||
|
|
||||||
|
## Переменные, не читаемые приложением (инфраструктура/сборка/тесты)
|
||||||
|
|
||||||
|
Присутствуют в `.docker/.env`, `.docker/.docker.env`, `docker-compose.yml` или Helm, но `config/config.go` их не разбирает.
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `API_PORT` | `.docker/.env` | Порт API для локального compose |
|
||||||
|
| `POSTGRES_EXTERNAL_PORT`, `POSTGRES_EXTERNAL_PORT_2` | `docker-compose.yml` | Проброс портов контейнеров Postgres |
|
||||||
|
| `POSTGRES_ADDRESS2` | `.docker/.docker.env` | Опечатка/легаси (нет суффикса `_`), кодом не читается |
|
||||||
|
| `GRPC_ADDRESS`, `GRPC_PORT` | `.docker/.env` | Объявлены, но кодом не используются |
|
||||||
|
| `TEST_BIM`, `JWT_TEST` | `.docker/.env`, `.docker/.docker.env` | Данные для интеграционных тестов |
|
||||||
|
| `POSTGRES_*_4`, `DB_CERT_PATH_4` | `.helm/values-*.yaml`, IaC `backend-deployment.yaml` | Четвёртый кластер задаётся в деплое, но `config.go` доходит только до суффикса `_3` |
|
||||||
|
| `LAST_SLAVE_2_BIM(_V3)`, `LAST_SLAVE_3_BIM(_V3)`, `LAST_SLAVE_4_BIM(_V3)` | `.helm/values-*.yaml` | Заданы в values, но кодом не читаются (актуальны только `LAST_MASTER_*` и `LAST_SLAVE_1_*`) |
|
||||||
|
| `CI_COMMIT_SHORT_SHA` | `.gitlab-ci.yml` (build-arg) | Тег/версия сборки образа |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта приложения (`.helm/values-<env>.yaml`)
|
||||||
|
|
||||||
|
Обычные значения задаются в блоке `envs` для каждого окружения (`stage`/`preprod`/`production`) и содержат те же переменные `POSTGRES_*`, `API_ADDRESS`, `DJANGO_HOST`, `ENABLE_SSL`, `ENABLE_SQL_QUERY`, `LAST_*`, что описаны выше (различаются адресами БД, размерами пула, границами шардирования и доменом Django).
|
||||||
|
|
||||||
|
Значения из секретов (блок `secrets`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secret_name`) | Ключ (`secret_key`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_USER` | `bim-v2-database-secret` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD` | `bim-v2-database-secret` | `password` |
|
||||||
|
| `POSTGRES_USER_2` | `bim-v2-database-secret-2` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD_2` | `bim-v2-database-secret-2` | `password` |
|
||||||
|
| `POSTGRES_USER_3` | `bim-v2-database-secret-3` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD_3` | `bim-v2-database-secret-3` | `password` |
|
||||||
|
| `POSTGRES_USER_4` | `bim-v2-database-secret-3` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD_4` | `bim-v2-database-secret-3` | `password` |
|
||||||
|
|
||||||
|
Ключевые не-env значения чарта: `api.name`, `api.image`, `api.version`, `api.port` (`8080`), `api.replicas`, `api.service_name`/`api.service_port` (`80`), `api.service_account`, `api.requests.memory`/`cpu`, `api.api_host`, `api.api_host_prefix` (`/bimv2/api/`), `api.api_path` (`/api/`), `api.internal_path` (`/internal/`), `api.permitted_ns` (namespace'ы, которым разрешён доступ к `/internal/*`), `imagePullSecrets`.
|
||||||
|
|
||||||
|
Сетевой слой (`.helm/templates/mesh-config.yaml`, Istio):
|
||||||
|
|
||||||
|
- `VirtualService` (при `api.virtual_service.enabled`) маршрутизирует `api_host_prefix` → `api_path`, задаёт CORS (`allowOrigins` — `*.sarex.io` и `localhost`, `allowHeaders` включают `Authorization`, `Content-Type`, `Identity`).
|
||||||
|
- `AuthorizationPolicy` `routes-v2`: доступ к `/api/*` разрешён только от istio-ingressgateway при наличии claim `token_type=access`; доступ к `/internal/*` — только из namespace'ов `api.permitted_ns`.
|
||||||
|
|
||||||
|
## Переменные из IaC-репозитория (`iac/apps/bim/`)
|
||||||
|
|
||||||
|
Деплой в этом репозитории (kustomize) устроен иначе, чем чарт приложения:
|
||||||
|
|
||||||
|
- `base/backend-deployment.yaml` — Deployment `backend` в namespace `bim`, образ `bim-api`, `containerPort: 8000`, `API_ADDRESS=0.0.0.0:8000`, health-проба `GET /ping`.
|
||||||
|
- Секреты PostgreSQL инъектируются **из Vault** (аннотации `vault.hashicorp.com/*`, роль `bim`, путь `secrets/data/postgresql/apps/bim`) в файл `/vault/secrets/bim-postgresql`, который экспортируется в окружение перед запуском (`set -a; . /vault/secrets/bim-postgresql; set +a; exec ./httpserver`). Шаблон Vault задаёт `POSTGRES_ADDRESS[_2../_4]`, `POSTGRES_PORT*`, `POSTGRES_DB*` (все указывают на `postgresql.bim.svc.cluster.local:5432`, БД `bim_db`) и `POSTGRES_USER*`/`POSTGRES_PASSWORD*` из Vault.
|
||||||
|
- Прочие env заданы прямо в манифесте: `LAST_MASTER_BIM`, `LAST_MASTER_BIM_V3`, `LAST_SLAVE_1_BIM`, `POSTGRES_POOL_SIZE`, `DB_CERT_PATH_2/3/4`, `DJANGO_HOST` (`http://backend.django.svc.cluster.local:8000`), `ENABLE_SQL_QUERY=0`, `ENABLE_SSL=0`.
|
||||||
|
- Оверлеи `brusnika-prod`, `brusnika-stage`, `yc-k8s-test` патчат base (реплики, образ, postgresql).
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны `generic/common-ci` (`universal-pipeline-<env>.yaml`, `common-security-scan.yaml`, `common-build.yaml`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ветка `master` | `preprod` | `bim-api-preprod` |
|
||||||
|
| ветка `stage` | `stage` | `bim-api-stage` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `bim-api-prod` |
|
||||||
|
|
||||||
|
Общие переменные job'ов: `RELEASE_NAME=bim-backend-v2`, `CHART_NAME=bim-backend-v2`, `CHART_VERSION=0.0.1-<stand>`, `IMAGE_PATH=api.image`, `DOCKERFILE_PATH=.docker/api.dockerfile`, `HELM_SET_ARGS=--set api.image=${IMAGE_NAME}`, `BUILD_ARGS=--build-arg CI_COMMIT_SHORT_SHA=...`, флаги `ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_STATE_UPDATE`/`ENABLE_DEPLOY=true`, `ENABLE_LINTER=false`. Стадии: `linter → test → unittest → prebuild-secscan → build → state-update → deploy`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- Переменные разбираются **без префикса** (`envconfig.Process("", ...)`) — имена совпадают с тегами `envconfig` полей структуры `Config`.
|
||||||
|
- Обязательность полей не валидируется: отсутствующая переменная молча получает нулевое значение. Например пустой `API_ADDRESS` приведёт к попытке слушать на `":0"`.
|
||||||
|
- В коде объявлены только три кластера (`POSTGRES_*`, `_2`, `_3`), тогда как в деплое присутствует и четвёртый (`_4`). Переменные `_4` и дополнительные `LAST_SLAVE_2/3/4_*` в окружении задаются, но приложением не используются.
|
||||||
|
- Приложение загружает `.env` автоматически (`godotenv.Load(".env")`); при отсутствии файла ошибки нет — берутся переменные процесса.
|
||||||
|
- В `NewConfig` ошибка `envconfig.Process` не возвращается, а вызывает `panic` (обёрнута в `sync.Once`).
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Postgres поднимается через `docker-compose` (`.docker/docker-compose.yml`), приложение — через `make api` и запуск `httpserver`. Минимально необходимо задать (готовые примеры — в `.env.example`):
|
||||||
|
|
||||||
|
- `API_ADDRESS`
|
||||||
|
- Master-кластер: `POSTGRES_ADDRESS`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_POOL_SIZE`
|
||||||
|
- Slave-кластеры `_2` и `_3` (те же поля) — сервис создаёт соединения ко всем трём при старте
|
||||||
|
- `ENABLE_SSL=0`, `DB_CERT_PATH*` (при `ENABLE_SSL=1`)
|
||||||
|
- `DJANGO_HOST`
|
||||||
|
- `LAST_MASTER_BIM`, `LAST_MASTER_BIM_V3`, `LAST_SLAVE_1_BIM`, `LAST_SLAVE_1_BIM_V3`
|
||||||
|
- `INTEGRATION_TESTS=0`, `ENABLE_SQL_QUERY` по необходимости
|
||||||
55
apps/bim/ENDPOINTS.md
Normal file
55
apps/bim/ENDPOINTS.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует bim-backend-v2
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается сам сервис `bim-backend-v2` (исходящие вызовы). Эндпоинты, которые сервис **предоставляет**, описаны в `openapi.yaml`.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Исходящие вызовы выполняются HTTP-клиентом на базе [`go-resty/resty`](https://github.com/go-resty/resty) в пакете `pkg/django_client` (`DjangoClient`). Клиент создаётся в `NewRestClient`:
|
||||||
|
|
||||||
|
- базовый хост — `SetHostURL(cfg.DjangoHost)` (переменная `DJANGO_HOST`);
|
||||||
|
- таймаут запроса — `SetTimeout(10 * time.Second)`;
|
||||||
|
- число повторов — `SetRetryCount(5)`.
|
||||||
|
|
||||||
|
Аутентификация проксируется: JWT пользователя извлекается из контекста запроса (`auth.JWTFromContext`), при необходимости отбрасывается схема `Bearer `, и токен передаётся во внешний сервис заголовком `Authorization: Bearer <jwt>` (`SetAuthToken` + `SetAuthScheme("Bearer")`). Заголовок `Content-Type: application/json`.
|
||||||
|
|
||||||
|
В режиме интеграционных тестов (`INTEGRATION_TESTS=1`) внешний вызов не выполняется — `CheckUserIsAdmin` возвращает `true`.
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значение берётся из переменной `DJANGO_HOST` (см. `CONFIGURATION.md`). Итоговый URL = `<DJANGO_HOST>` + `path` эндпоинта.
|
||||||
|
|
||||||
|
| Сервис | Назначение | Значение `DJANGO_HOST` |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `django` (Sarex backend) | Проверка прав пользователя (админ/не админ) | локально/`stage`: `https://stage.sarex.io`; `preprod`: `https://lk.preprod.sarex.io`; `prod`: `https://lk.sarex.io`; контур (IaC): `http://backend.django.svc.cluster.local:8000` |
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `django` — Sarex backend
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `api/client/settings/` | Получить настройки текущего пользователя. Используется поле `is_admin` (проверка прав администратора в `CheckUserIsAdmin`) |
|
||||||
|
|
||||||
|
Детали вызова `GET api/client/settings/`:
|
||||||
|
|
||||||
|
| Параметр | Значение |
|
||||||
|
| --- | --- |
|
||||||
|
| Заголовки | `Authorization: Bearer <jwt пользователя>`, `Content-Type: application/json` |
|
||||||
|
| Тело запроса | нет |
|
||||||
|
| Ожидаемый ответ | `{ "is_admin": bool }` (структура `userSettingsFromDjango`) |
|
||||||
|
| Успешные статусы | `200`, `201` |
|
||||||
|
| Поведение при ошибке | Любая ошибка транспорта, `resp == nil` или статус вне `200/201` логируется, метод трактует пользователя как **не администратора** (`false`) |
|
||||||
|
|
||||||
|
## Где используется
|
||||||
|
|
||||||
|
Проверка `CheckUserIsAdmin` вызывается в обработчиках, изменяющих модели статусов (требуют прав администратора). При отсутствии прав такие эндпоинты возвращают `403 No admin rights`:
|
||||||
|
|
||||||
|
- `POST /api/v1/bims/{bim_id}/status_model` — создание модели статусов BIM;
|
||||||
|
- `DELETE /api/v1/bims/{bim_id}/delete_status_model` — удаление модели статусов BIM;
|
||||||
|
- `POST /api/v1/companies/{company_id}/status_model` — создание модели статусов компании;
|
||||||
|
- `DELETE /api/v1/companies/{company_id}/status_model` — удаление модели статусов компании.
|
||||||
|
|
||||||
|
## Замечания
|
||||||
|
|
||||||
|
- Единственная внешняя HTTP-зависимость сервиса — Django-бэкенд (`DJANGO_HOST`). Прочие интеграции (PostgreSQL, pprof) не являются HTTP-вызовами к внешним REST-сервисам.
|
||||||
|
- Подпись JWT самим сервисом не проверяется — проброшенный токен просто пересылается в Django, который и выполняет авторизацию (см. также раздел «Аутентификация» в `openapi.yaml`).
|
||||||
1158
apps/bim/openapi.yaml
Normal file
1158
apps/bim/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
80
apps/cde/.env.example
Normal file
80
apps/cde/.env.example
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# cde-orchestration-demo (Оркестратор) — пример переменных окружения
|
||||||
|
#
|
||||||
|
# Разбор переменных: github.com/sethvargo/go-envconfig.
|
||||||
|
# Файл .env подгружается через godotenv (флаг -env-file, по умолчанию `.env`).
|
||||||
|
# Глобального префикса у переменных НЕТ (в отличие от других сервисов Sarex).
|
||||||
|
# Вложенные секции задаются префиксом в env-тегах (напр. DATABASE_URL).
|
||||||
|
#
|
||||||
|
# Значения ниже — примеры и значения по умолчанию из кода. Секреты (ключи,
|
||||||
|
# пароли, токены) оставлены пустыми — заполните собственными.
|
||||||
|
# Один и тот же .env используется всеми бинарниками (см. docker-compose.yml),
|
||||||
|
# но каждый бинарник читает только нужное ему подмножество (см. CONFIGURATION.md).
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- Общие ---
|
||||||
|
ENVIRONMENT=production
|
||||||
|
LOG_LEVEL=info # cmd/http: default=info; воркеры: default=debug
|
||||||
|
IS_CONTOUR=false
|
||||||
|
|
||||||
|
# --- HTTP-сервер (cmd/http) ---
|
||||||
|
ADDRESS=:8080
|
||||||
|
PROCESS_CACHE_TTL=60
|
||||||
|
PROCESS_CACHE_CLEAR_INTERVAL=60
|
||||||
|
PUBLIC_KEY= # PEM RSA public key для проверки JWT (обязателен для http)
|
||||||
|
OPERATE_URL= # Базовый URL Camunda Operate/REST
|
||||||
|
SAREX_BACKEND_BASE_URL=https://stage.sarex.io
|
||||||
|
|
||||||
|
# --- Camunda ---
|
||||||
|
CAMUNDA_KEYCLOAK_URL=
|
||||||
|
CAMUNDA_CLIENT_ID=operate # только cmd/http
|
||||||
|
CAMUNDA_CLIENT_SECRET=identity-secret-for-components # только cmd/http
|
||||||
|
CAMUNDA_PROCESS_DEFINITION_ID=actionsOnApproval # только cmd/http
|
||||||
|
|
||||||
|
# --- Zeebe ---
|
||||||
|
ZEEBE_GATEWAY=
|
||||||
|
ZEEBE_CLIENT_ID=zeebe
|
||||||
|
ZEEBE_CLIENT_SECRET=identity-secret-for-components
|
||||||
|
ZEEBE_WORKER_JOB_TYPE=markDocuments # у каждого воркера своё значение по умолчанию (см. CONFIGURATION.md)
|
||||||
|
|
||||||
|
# --- Database (PostgreSQL) ---
|
||||||
|
DATABASE_URL=
|
||||||
|
DATABASE_POOL_SIZE=10
|
||||||
|
|
||||||
|
# --- S3 ---
|
||||||
|
S3_ENDPOINT_URL=https://storage.yandexcloud.net
|
||||||
|
S3_ACCESS_KEY_ID=
|
||||||
|
S3_SECRET_ACCESS_KEY=
|
||||||
|
S3_PARTITION_ID=yc
|
||||||
|
S3_SIGNING_REGION=ru-central1
|
||||||
|
|
||||||
|
# --- Auth (без префикса) ---
|
||||||
|
AUTH_HOST=
|
||||||
|
USERNAME=
|
||||||
|
PASSWORD=
|
||||||
|
|
||||||
|
# --- Flows (сервис рабочих процессов) ---
|
||||||
|
FLOWS_URL=
|
||||||
|
FLOWS_INTERNAL_URL=
|
||||||
|
|
||||||
|
# --- Workspaces ---
|
||||||
|
WORKSPACES_URL=
|
||||||
|
|
||||||
|
# --- Workflows (используется воркером split_pdf) ---
|
||||||
|
WORKFLOWS_HOST=
|
||||||
|
WORKFLOWS_IMAGE_TAG=latest
|
||||||
|
|
||||||
|
# --- System log ---
|
||||||
|
SYSTEM_LOG_URL=
|
||||||
|
|
||||||
|
# --- Telegram (алертинг воркеров) ---
|
||||||
|
TELEGRAM_TOKEN=
|
||||||
|
TELEGRAM_ALERT_GROUP_ID=
|
||||||
|
TELEGRAM_DEBUG=false
|
||||||
|
|
||||||
|
# --- AMQP / RabbitMQ (markings v2, copy v2) ---
|
||||||
|
AMQP_HOST=
|
||||||
|
AMQP_PORT=
|
||||||
|
AMQP_USER=
|
||||||
|
AMQP_PASSWORD=
|
||||||
|
AMQP_PATH_API=
|
||||||
215
apps/cde/CONFIGURATION.md
Normal file
215
apps/cde/CONFIGURATION.md
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
# Конфигурация проекта cde-orchestration-demo (Оркестратор)
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется библиотекой [`github.com/sethvargo/go-envconfig`](https://github.com/sethvargo/go-envconfig): в каждом бинарнике вызывается `envconfig.Process(ctx, config)` со своей структурой `Config` (см. `internal/app/http/config.go` и `internal/app/worker/*/config.go`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **Глобального префикса нет** — в отличие от других сервисов Sarex, переменные не имеют общего префикса (напр. просто `LOG_LEVEL`, `DATABASE_URL`).
|
||||||
|
- Вложенные секции задаются тегом `env:", prefix=XXX_"` на поле-структуре. Например поле `Database DatabaseConfig` с `prefix=DATABASE_` и полем `Url` с тегом `env:"URL"` даёт переменную `DATABASE_URL`.
|
||||||
|
- Структура `Auth` **не имеет** тега `prefix`, поэтому её поля читаются без префикса: `AUTH_HOST`, `USERNAME`, `PASSWORD`.
|
||||||
|
- Значения по умолчанию задаются в теге через `default=...`. Отсутствие поля без дефолта не приводит к ошибке `envconfig` (пустое значение), но может привести к падению при инициализации зависимого клиента (напр. пустой `PUBLIC_KEY` вызовет панику при старте http).
|
||||||
|
|
||||||
|
Файл `.env` подгружается через [`github.com/lpernett/godotenv`](https://github.com/lpernett/godotenv): в `main` вызывается `godotenv.Load(*envFileFlag)`, путь задаётся флагом `-env-file` (по умолчанию `.env`). Если файла нет — загрузка пропускается, переменные берутся из окружения процесса.
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Флаг `-env-file` → `.env` (godotenv) и/или переменные окружения процесса |
|
||||||
|
| Локально (docker-compose) | `docker-compose.yml`: у каждого сервиса `env_file: .env` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`: блок `envs` (обычные значения) и `secretEnvs` (значения из k8s-секрета `cde-secret`) чарта `universal-chart` |
|
||||||
|
| Kubernetes (kustomize, этот репозиторий) | Vault Agent инжектит секрет `secrets/data/vault/apps/cde` в файл `/vault/secrets/cde-env`, который экспортируется в окружение перед запуском бинарника (`source /vault/secrets/cde-env`) |
|
||||||
|
|
||||||
|
## Бинарники (точки входа)
|
||||||
|
|
||||||
|
| Бинарник | Точка входа | Config | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `http` | `cmd/http/main.go` | `internal/app/http` | HTTP API оркестрации (процессы, подпись, загрузка BPMN) |
|
||||||
|
| `copy` | `cmd/worker/copy/main.go` | `.../worker/copy` | Воркер копирования документов (`copyDocuments`) |
|
||||||
|
| `copyv2` | `cmd/worker/copyv2/main.go` | `.../worker/copyv2` | Копирование документов v2 (`copyDocumentsv2`) |
|
||||||
|
| `create_versions` | `cmd/worker/create_versions/main.go` | `.../worker/create_versions` | Создание версий (`createVersions`) |
|
||||||
|
| `create_versionsv2` | `cmd/worker/create_versionsv2/main.go` | `.../worker/create_versionsv2` | Создание версий v2 (`createVersionsv2`) |
|
||||||
|
| `flows_callback` | `cmd/worker/flows_callback/main.go` | `.../worker/flows_callback` | Обратный вызов в сервис flows (`flowsCallback`) |
|
||||||
|
| `markings` | `cmd/worker/markings/main.go` | `.../worker/markings` | Маркировка документов (`markDocuments`) |
|
||||||
|
| `markingsv2` | `cmd/worker/markingsv2/main.go` | `.../worker/markingsv2` | Маркировка v2 (`markDocumentsv2`) |
|
||||||
|
| `sign` | `cmd/worker/sign/main.go` | `.../worker/sign` | Подпись документов (`signDocuments`) |
|
||||||
|
| `signv2` | `cmd/worker/signv2/main.go` | `.../worker/signv2` | Подпись v2 (`signDocumentsv2`) |
|
||||||
|
| `split_pdf` | `cmd/worker/split_pdf/main.go` | `.../worker/split_pdf` | Разбиение/обработка PDF (`splitPDF`) |
|
||||||
|
| `update_bundles` | `cmd/worker/update_bundles/main.go` | `.../worker/update_bundles` | Обновление бандлов (`updateBundles`) |
|
||||||
|
|
||||||
|
Воркеры — это Zeebe job-workers: они подключаются к Zeebe-gateway и обрабатывают Service Task соответствующего типа (`ZEEBE_WORKER_JOB_TYPE`). HTTP-сервер, помимо приёма запросов, обращается к Camunda Operate/Zeebe (см. `ENDPOINTS.md`).
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что значения по умолчанию нет.
|
||||||
|
|
||||||
|
### Общие (для всех бинарников)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENVIRONMENT` | string | `production` (у воркеров) | Окружение развёртывания. У `http` не читается |
|
||||||
|
| `LOG_LEVEL` | string | `info` (http) / `debug` (воркеры) | Уровень логирования |
|
||||||
|
| `IS_CONTOUR` | bool | `false` | Режим изолированного контура (влияет на инициализацию S3) |
|
||||||
|
|
||||||
|
### HTTP-сервер (`cmd/http`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ADDRESS` | string | `:8080` | Адрес прослушивания Fiber |
|
||||||
|
| `PROCESS_CACHE_TTL` | int (сек) | `60` | TTL кеша процессов |
|
||||||
|
| `PROCESS_CACHE_CLEAR_INTERVAL` | int (сек) | `60` | Интервал очистки кеша процессов |
|
||||||
|
| `PUBLIC_KEY` | string (PEM) | — | RSA public key для проверки JWT. Обязателен: при пустом/некорректном значении сервис падает при старте |
|
||||||
|
| `OPERATE_URL` | string | — | Базовый URL Camunda Operate/REST |
|
||||||
|
| `SAREX_BACKEND_BASE_URL` | string | — | Базовый URL sarex-backend (проверка MRPA при подписи) |
|
||||||
|
|
||||||
|
### Camunda (`CAMUNDA_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение | Где используется |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `CAMUNDA_KEYCLOAK_URL` | string | — | URL Keycloak для OAuth (Zeebe/Operate) | http + воркеры |
|
||||||
|
| `CAMUNDA_CLIENT_ID` | string | `operate` | Client ID для Operate | только http |
|
||||||
|
| `CAMUNDA_CLIENT_SECRET` | string | `identity-secret-for-components` | Client secret для Operate | только http |
|
||||||
|
| `CAMUNDA_PROCESS_DEFINITION_ID` | string | `actionsOnApproval` | ID определения процесса | только http |
|
||||||
|
|
||||||
|
### Zeebe (`ZEEBE_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ZEEBE_GATEWAY` | string | — | Адрес Zeebe gateway |
|
||||||
|
| `ZEEBE_CLIENT_ID` | string | `zeebe` | Client ID |
|
||||||
|
| `ZEEBE_CLIENT_SECRET` | string | `identity-secret-for-components` | Client secret |
|
||||||
|
| `ZEEBE_WORKER_JOB_TYPE` | string | зависит от воркера | Тип Service Task, который слушает воркер |
|
||||||
|
|
||||||
|
Значения `ZEEBE_WORKER_JOB_TYPE` по умолчанию: `markDocuments` (http/markings), `markDocumentsv2` (markingsv2), `copyDocuments` (copy), `copyDocumentsv2` (copyv2), `createVersions` (create_versions), `createVersionsv2` (create_versionsv2), `flowsCallback` (flows_callback), `signDocuments` (sign), `signDocumentsv2` (signv2), `splitPDF` (split_pdf), `updateBundles` (update_bundles).
|
||||||
|
|
||||||
|
### Database (`DATABASE_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DATABASE_URL` | string | — | DSN подключения к PostgreSQL (pgx) |
|
||||||
|
| `DATABASE_POOL_SIZE` | int32 | `10` | Размер пула соединений |
|
||||||
|
|
||||||
|
### S3 (`S3_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `S3_ENDPOINT_URL` | string | `https://storage.yandexcloud.net` | Эндпоинт S3 |
|
||||||
|
| `S3_ACCESS_KEY_ID` | string | — | Access key |
|
||||||
|
| `S3_SECRET_ACCESS_KEY` | string | — | Secret key |
|
||||||
|
| `S3_PARTITION_ID` | string | `yc` | Partition ID (aws-sdk-go-v2) |
|
||||||
|
| `S3_SIGNING_REGION` | string | `ru-central1` | Регион для подписи запросов |
|
||||||
|
|
||||||
|
### Auth (без префикса)
|
||||||
|
|
||||||
|
Поле-структура `Auth` не имеет префикса, поэтому переменные читаются напрямую.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `AUTH_HOST` | string | — | Хост сервиса аутентификации (получение токенов пользователя/админа) |
|
||||||
|
| `USERNAME` | string | — | Логин админ-учётки |
|
||||||
|
| `PASSWORD` | string | — | Пароль админ-учётки |
|
||||||
|
|
||||||
|
### Flows (`FLOWS_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `FLOWS_URL` | string | — | Базовый URL сервиса flows |
|
||||||
|
| `FLOWS_INTERNAL_URL` | string | — | Внутренний URL flows (обновление документов review). Есть только в конфигах `copy`/`copyv2` |
|
||||||
|
|
||||||
|
### Workspaces (`WORKSPACES_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `WORKSPACES_URL` | string | — | URL сервиса рабочих областей (используется воркерами copy/copyv2) |
|
||||||
|
|
||||||
|
### Workflows (`WORKFLOWS_*`)
|
||||||
|
|
||||||
|
Используется воркером `split_pdf`.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `WORKFLOWS_HOST` | string | — | Хост сервиса workflows |
|
||||||
|
| `WORKFLOWS_IMAGE_TAG` | string | `latest` | Тег docker-образа задач обработки PDF |
|
||||||
|
|
||||||
|
> Container registry (`cr.yandex/crp3ccidau046kdj8g9q`) и флаг `UploadResultsToS3=true` заданы в коде воркера `split_pdf` (`worker.go`), а не через окружение.
|
||||||
|
|
||||||
|
### System log (`SYSTEM_LOG_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `SYSTEM_LOG_URL` | string | — | URL сервиса системных логов (copy, copyv2, create_versions, create_versionsv2) |
|
||||||
|
|
||||||
|
### Telegram (`TELEGRAM_*`)
|
||||||
|
|
||||||
|
Клиент алертинга для воркеров.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `TELEGRAM_TOKEN` | string | — | Токен бота |
|
||||||
|
| `TELEGRAM_ALERT_GROUP_ID` | int64 | — | ID группы для алертов |
|
||||||
|
| `TELEGRAM_DEBUG` | bool | `false` | Debug-режим бота |
|
||||||
|
|
||||||
|
### AMQP / RabbitMQ (`AMQP_*`)
|
||||||
|
|
||||||
|
Используется воркерами `markingsv2` и `copyv2` (маркировка бандлов через RabbitMQ).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `AMQP_HOST` | string | — | Хост RabbitMQ |
|
||||||
|
| `AMQP_PORT` | string | — | Порт RabbitMQ |
|
||||||
|
| `AMQP_USER` | string | — | Пользователь |
|
||||||
|
| `AMQP_PASSWORD` | string | — | Пароль |
|
||||||
|
| `AMQP_PATH_API` | string | — | Vhost / путь API в URL подключения |
|
||||||
|
|
||||||
|
## Матрица «переменная → бинарник»
|
||||||
|
|
||||||
|
| Секция | http | copy | copyv2 | create_versions | create_versionsv2 | flows_callback | markings | markingsv2 | sign | signv2 | split_pdf | update_bundles |
|
||||||
|
| --- | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| Общие | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| `ADDRESS`/`PROCESS_CACHE_*` | ✓ | | | | | | | | | | | |
|
||||||
|
| `PUBLIC_KEY`,`OPERATE_URL`,`SAREX_BACKEND_BASE_URL`,`CAMUNDA_CLIENT_*`,`CAMUNDA_PROCESS_DEFINITION_ID` | ✓ | | | | | | | | | | | |
|
||||||
|
| `CAMUNDA_KEYCLOAK_URL`,`ZEEBE_*` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| `DATABASE_*` | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ | ✓ | ✓ | |
|
||||||
|
| `S3_*` | ✓ | ✓ | ✓ | ✓ | ✓ | | | ✓ | ✓ | ✓ | | |
|
||||||
|
| `AUTH_*`/`USERNAME`/`PASSWORD` | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| `FLOWS_URL` | | ✓ | ✓ | | | ✓ | ✓ | ✓ | ✓ | ✓ | | ✓ |
|
||||||
|
| `FLOWS_INTERNAL_URL` | | ✓ | ✓ | | | | | | | | | |
|
||||||
|
| `WORKSPACES_URL` | | ✓ | ✓ | | | | | | | | | |
|
||||||
|
| `WORKFLOWS_*` | | | | | | | | | | | ✓ | |
|
||||||
|
| `SYSTEM_LOG_URL` | | ✓ | ✓ | ✓ | ✓ | | | | | | | |
|
||||||
|
| `AMQP_*` | | | ✓ | | | | | ✓ | | | | |
|
||||||
|
| `TELEGRAM_*` | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
> Матрица построена по структурам `Config` соответствующих бинарников. Наличие поля в структуре не всегда означает, что клиент инициализируется — см. замечания ниже.
|
||||||
|
|
||||||
|
## Переменные в Helm-чарте (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Обычные значения (блок `envs`):
|
||||||
|
|
||||||
|
| Переменная | Значения по окружениям |
|
||||||
|
| --- | --- |
|
||||||
|
| `SAREX_BACKEND_BASE_URL` | stage: `https://stage.sarex.io`, preprod: `https://preprod.sarex.io`, production: `https://lk.sarex.io` |
|
||||||
|
|
||||||
|
Значения из секрета (блок `secretEnvs`, общий для всех сервисов через якорь `*cde_secret_envs`), берутся из k8s-секрета `cde-secret` одноимёнными ключами: `ENVIRONMENT`, `LOG_LEVEL`, `ZEEBE_GATEWAY`, `DATABASE_URL`, `S3_ENDPOINT_URL`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `PUBLIC_KEY`, `PDM_URL`, `FLOWS_URL`, `FLOWS_INTERNAL_URL`, `USERNAME`, `PASSWORD`, `CAMUNDA_PROCESS_DEFINITION_ID`, `OPERATE_URL`, `CAMUNDA_KEYCLOAK_URL`, `CAMUNDA_CLIENT_ID`, `CAMUNDA_CLIENT_SECRET`, `WORKFLOWS_HOST`, `WORKSPACES_URL`, `AUTH_HOST`, `TELEGRAM_ALERT_GROUP_ID`, `TELEGRAM_TOKEN`, `IS_CONTOUR`, `AMQP_HOST`, `AMQP_PORT`, `AMQP_USER`, `AMQP_PASSWORD`, `AMQP_PATH_API`, `SYSTEM_LOG_URL`.
|
||||||
|
|
||||||
|
## Развёртывание через kustomize (этот репозиторий)
|
||||||
|
|
||||||
|
В `iac/apps/cde` секреты доставляются не через `secretEnvs` чарта, а через **Vault Agent Injector**: аннотации подов монтируют секрет `secrets/data/vault/apps/cde` в файл `/vault/secrets/cde-env`, который экспортируется перед запуском (`source /vault/secrets/cde-env`, затем `exec /http` или `/worker`). Дополнительно контейнерам задаётся `S3_IS_CONTOUR=true` (примечание: в коде используется переменная `IS_CONTOUR`).
|
||||||
|
|
||||||
|
Оверлеи: `base` (общие манифесты), `brusnika-stage`, `brusnika-prod`, `yc-k8s-test`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **Нет глобального префикса.** Имена переменных короткие (`USERNAME`, `PASSWORD`, `AUTH_HOST`) — легко пересечься с системными; следите за окружением процесса.
|
||||||
|
- **`PUBLIC_KEY` обязателен для `http`** — при пустом/некорректном PEM сервис паникует на старте (`server.go`).
|
||||||
|
- **`PDM_URL`** присутствует в `secretEnvs` Helm, но соответствующий клиент (`internal/adapters/http/pdm`) в текущей сборке нигде не инициализируется — переменная фактически не используется кодом.
|
||||||
|
- **`S3_IS_CONTOUR`** задаётся в kustomize-манифестах, тогда как код читает `IS_CONTOUR` (без префикса `S3_`). Проверьте, что для влияния на поведение выставлен именно `IS_CONTOUR`.
|
||||||
|
- **`FLOWS_INTERNAL_URL`** объявлен только в конфигах `copy`/`copyv2`; в остальных воркерах поля нет, хотя ключ есть в общем секрете.
|
||||||
|
- **Значения по умолчанию для секретов Camunda/Zeebe** (`identity-secret-for-components`) подходят для локального стенда, но должны переопределяться в prod.
|
||||||
|
- Приложение читает `.env` только если файл существует; иначе используются переменные окружения. `make`-целей для генерации `.env` в репозитории нет — используйте этот `.env.example` как шаблон.
|
||||||
130
apps/cde/ENDPOINTS.md
Normal file
130
apps/cde/ENDPOINTS.md
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Эндпоинты внешних сервисов, с которыми взаимодействует cde-orchestration-demo
|
||||||
|
|
||||||
|
Документ описывает все HTTP/AMQP/gRPC-эндпоинты внешних сервисов, к которым обращается оркестратор (сервер `cmd/http` и воркеры). Это исходящие вызовы; описание API, который оркестратор **предоставляет**, — в `openapi.yaml`.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Клиенты внешних сервисов лежат в `internal/adapters/http/*` и `internal/adapters/*`. Базовые URL берутся из переменных окружения (см. `CONFIGURATION.md`). Для HTTP используются два клиента: Fiber `client` (camunda, flows, pdm, workspaces, system_log) и `go-resty` (workflows, sarexbackend). Аутентификация — по-разному в зависимости от сервиса (OAuth client_credentials, Bearer-токен пользователя/админа, Basic).
|
||||||
|
|
||||||
|
## Базовые адреса по сервисам
|
||||||
|
|
||||||
|
| Сервис | Переменная базового адреса | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Camunda Operate / REST | `OPERATE_URL` | Fiber | Управление инстансами процессов, переменными, сообщениями |
|
||||||
|
| Camunda Keycloak | `CAMUNDA_KEYCLOAK_URL` | Fiber | OAuth-токен для Operate |
|
||||||
|
| Zeebe Gateway | `ZEEBE_GATEWAY` | gRPC (SDK) | Деплой BPMN, обработка job'ов воркерами |
|
||||||
|
| Auth (токены) | `AUTH_HOST` | Fiber/resty | Токены пользователя/админа для flows и pdm |
|
||||||
|
| Flows | `FLOWS_URL`, `FLOWS_INTERNAL_URL` | Fiber | Ревью, действия пользователя, обновление бандлов/документов |
|
||||||
|
| PDM | `PDM_URL` | Fiber | Маркировка бандлов *(клиент не подключён — см. примечание)* |
|
||||||
|
| Workflows | `WORKFLOWS_HOST` | resty | Создание workflow обработки PDF |
|
||||||
|
| Workspaces | `WORKSPACES_URL` | Fiber | Создание рабочих областей |
|
||||||
|
| System log | `SYSTEM_LOG_URL` | Fiber | Отправка системных логов |
|
||||||
|
| Sarex backend | `SAREX_BACKEND_BASE_URL` | resty | Получение MRPA по id |
|
||||||
|
| Telegram Bot API | `TELEGRAM_TOKEN` | tgbotapi | Алертинг воркеров |
|
||||||
|
| RabbitMQ (AMQP) | `AMQP_*` | amqp091 | Маркировка бандлов (RPC), вычисление хеш-сумм |
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### Camunda Operate / REST (`OPERATE_URL`, `CAMUNDA_KEYCLOAK_URL`)
|
||||||
|
|
||||||
|
`internal/adapters/http/camunda/client.go`. Все запросы (кроме получения токена) идут с заголовком `Authorization: Bearer <access_token>`.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{CAMUNDA_KEYCLOAK_URL}/auth/realms/camunda-platform/protocol/openid-connect/token` | OAuth-токен (`grant_type=client_credentials`) |
|
||||||
|
| POST | `{OPERATE_URL}/v2/process-instances` | Создать инстанс процесса |
|
||||||
|
| GET | `{OPERATE_URL}/api/process-instances/{key}` | Получить инстанс процесса |
|
||||||
|
| POST | `{OPERATE_URL}/v1/variables/search` | Поиск переменных процесса по имени/значению |
|
||||||
|
| GET | `{OPERATE_URL}/api/process-instances/{key}/variables/{varId}` | Значение конкретной переменной |
|
||||||
|
| POST | `{OPERATE_URL}/api/process-instances/{key}/variables` | Список переменных инстанса (`scopeId`) |
|
||||||
|
| POST | `{OPERATE_URL}/v2/messages/publication` | Публикация сообщения процессу (напр. `signRequest`) |
|
||||||
|
|
||||||
|
### Zeebe Gateway (`ZEEBE_GATEWAY`)
|
||||||
|
|
||||||
|
`internal/adapters/zeebe`. gRPC через официальный SDK `camunda/zeebe/clients/go/v8`. Используется для деплоя определений процессов (`NewProcessDefinition`) и для job-воркеров, которые слушают Service Task типа `ZEEBE_WORKER_JOB_TYPE` и по завершении/ошибке возвращают результат в инстанс процесса.
|
||||||
|
|
||||||
|
### Auth — токены (`AUTH_HOST`)
|
||||||
|
|
||||||
|
Используется адаптерами flows и pdm для получения Bearer-токенов.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `{AUTH_HOST}/token/user/{userID}/` | Токен от имени пользователя |
|
||||||
|
| POST | `{AUTH_HOST}/token/` | Токен админа (`username`/`password`) |
|
||||||
|
|
||||||
|
### Flows (`FLOWS_URL`, `FLOWS_INTERNAL_URL`)
|
||||||
|
|
||||||
|
`internal/adapters/http/flows/adapter.go`. Запросы (кроме `update-documents`) идут с `Authorization: Bearer <token>`; часть операций — с ретраями (экспоненциальный backoff).
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{FLOWS_URL}/user-actions/` | Добавить действие в историю |
|
||||||
|
| PATCH | `{FLOWS_URL}/reviews/{reviewID}/approve/` | Утвердить review (со статусом/комментарием) |
|
||||||
|
| PATCH | `{FLOWS_URL}/reviews/{reviewID}/update-bundles/` | Обновить бандлы review |
|
||||||
|
| PATCH | `{FLOWS_INTERNAL_URL}/reviews/{reviewID}/update-documents/` | Обновить документы review (внутренний URL, без авторизации) |
|
||||||
|
| GET | `{FLOWS_URL}/reviews/{reviewID}/documents/` | История бандлов документов review |
|
||||||
|
|
||||||
|
### PDM (`PDM_URL`, `AUTH_HOST`) — не подключён
|
||||||
|
|
||||||
|
`internal/adapters/http/pdm/client.go`. Клиент реализован, но в текущей сборке нигде не инициализируется (см. примечание в конце). Для полноты — какие вызовы он делает:
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `{AUTH_HOST}/token/user/{userID}/` | Токен пользователя (Basic auth логин/пароль) |
|
||||||
|
| PUT | `{PDM_URL}/bundles/{bundleID}/marks` | Проставить маркировки бандлу |
|
||||||
|
|
||||||
|
### Workflows (`WORKFLOWS_HOST`, `AUTH_HOST`)
|
||||||
|
|
||||||
|
`internal/adapters/http/workflows/adapter.go` (resty). Используется воркером `split_pdf`.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{WORKFLOWS_HOST}/internal/v1/companies/{companyID}/workflows` | Создать workflow обработки/оптимизации PDF |
|
||||||
|
|
||||||
|
> Базовый URL resty-клиента установлен в `AUTH_HOST`, а адрес workflows подставляется полным (`WORKFLOWS_HOST`). В параметрах задач передаётся `django_host = AUTH_HOST`.
|
||||||
|
|
||||||
|
### Workspaces (`WORKSPACES_URL`)
|
||||||
|
|
||||||
|
`internal/adapters/http/workspaces/client.go`. Используется воркерами `copy`/`copyv2`.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{WORKSPACES_URL}/internal/v2/workspaces` | Создать рабочую область |
|
||||||
|
|
||||||
|
### System log (`SYSTEM_LOG_URL`)
|
||||||
|
|
||||||
|
`internal/adapters/http/system_log/client.go`. Используется воркерами `copy`, `copyv2`, `create_versions`, `create_versionsv2`.
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `{SYSTEM_LOG_URL}/api/v0/system_log` | Отправить пакет системных логов |
|
||||||
|
|
||||||
|
### Sarex backend (`SAREX_BACKEND_BASE_URL`)
|
||||||
|
|
||||||
|
`internal/adapters/sarexbackend/client.go` (resty). Используется HTTP-сервером при подписи (проверка MRPA). Токены проксируются из входящего запроса (`Authorization`, опц. `Identity`).
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `{SAREX_BACKEND_BASE_URL}/api/core/mrpa/{id}/` | Получить MRPA по id |
|
||||||
|
|
||||||
|
### Telegram Bot API (`TELEGRAM_TOKEN`, `TELEGRAM_ALERT_GROUP_ID`)
|
||||||
|
|
||||||
|
`internal/adapters/http/telegram/client.go` через `go-telegram-bot-api`. Отправка алертов в заданную группу при ошибках/паниках в задачах воркеров.
|
||||||
|
|
||||||
|
### RabbitMQ / AMQP (`AMQP_*`)
|
||||||
|
|
||||||
|
Подключение вида `amqp://{USER}:{PASSWORD}@{HOST}:{PORT}/{PATH_API}`.
|
||||||
|
|
||||||
|
| Адаптер | Назначение |
|
||||||
|
| --- | --- |
|
||||||
|
| `internal/adapters/amqp/markings` | Маркировка бандла и получение его хеш-суммы (RPC поверх временной очереди, `correlation_id`). Используется воркерами `markingsv2`/`copyv2` |
|
||||||
|
| `internal/adapters/amqp/rpc` | Общий RPC-клиент RabbitMQ (переподключение, вычисление хеш-сумм объектов) |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Каждый адаптер проверяет `StatusCode()` ответа и оборачивает не-`200 OK` в ошибку с телом ответа (`internal/errors`). Отдельно у sarexbackend маппинг: `404 → ErrNotFound`, `403 → ErrForbidden`, прочие → generic. Адаптеры flows и pdm выполняют ретраи с экспоненциальным backoff (до 5 попыток).
|
||||||
|
|
||||||
|
## Примечания
|
||||||
|
|
||||||
|
- **PDM-клиент не подключён.** `internal/adapters/http/pdm` реализован, а переменная `PDM_URL` присутствует в Helm-секрете, но `pdm.New(...)` не вызывается ни в одном бинарнике. Раздел оставлен для полноты; при фактическом использовании актуализируйте документ.
|
||||||
|
- Пути даны относительно базовых URL из окружения; итоговый URL = `<базовый адрес>` + `путь`.
|
||||||
298
apps/cde/openapi.yaml
Normal file
298
apps/cde/openapi.yaml
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
|
||||||
|
info:
|
||||||
|
title: CDE Orchestration API
|
||||||
|
version: "0.0.0"
|
||||||
|
description: |
|
||||||
|
HTTP API оркестратора **cde-orchestration-demo**
|
||||||
|
(`gitlab.com/sarex-team/cde-orchestration-demo`) — управление процессами
|
||||||
|
согласования/подписи документов в Camunda (Zeebe/Operate).
|
||||||
|
|
||||||
|
Сервис написан на Go (**Fiber v3**), точка входа — `cmd/http/main.go`,
|
||||||
|
сборка приложения — `internal/app/http/server.go`. Все ручки объявлены в
|
||||||
|
`internal/controller/http/v0` и смонтированы под префиксом `/api`.
|
||||||
|
|
||||||
|
### Аутентификация
|
||||||
|
Все эндпоинты проходят через middleware `pkg/http/middleware/auth.go`.
|
||||||
|
Токен передаётся заголовком `Authorization: Bearer <jwt>`. Поддерживаются
|
||||||
|
два режима:
|
||||||
|
|
||||||
|
1. **Sarex** (по умолчанию) — подпись JWT проверяется RSA public key из
|
||||||
|
переменной `PUBLIC_KEY`.
|
||||||
|
2. **Zitadel** — если передан дополнительный заголовок
|
||||||
|
`Identity: Bearer <jwt>`, полезная нагрузка берётся из метаданных
|
||||||
|
этого токена (`urn:zitadel:iam:user:metadata`); подпись основным
|
||||||
|
сервисом не проверяется.
|
||||||
|
|
||||||
|
При отсутствии/некорректности заголовков middleware возвращает `401`.
|
||||||
|
|
||||||
|
### Замечания
|
||||||
|
- Ручка `GET /api/process/{instance_key}` может вернуть `425 Too Early`,
|
||||||
|
если процесс есть в кеше, но ещё не создан в Camunda (идёт обработка).
|
||||||
|
- Тело ответов на запись (`process`, `sign`, `operate`) обычно пустое —
|
||||||
|
значим только HTTP-статус.
|
||||||
|
|
||||||
|
contact:
|
||||||
|
name: cde-orchestration-demo
|
||||||
|
url: https://gitlab.com/sarex-team/cde-orchestration-demo
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: http://localhost:8080/api
|
||||||
|
description: Локальный запуск (Fiber, ADDRESS по умолчанию :8080)
|
||||||
|
- url: http://cde-svc.cde.svc.cluster.local/api
|
||||||
|
description: Внутрикластерный адрес (ClusterIP)
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: infra
|
||||||
|
description: Служебные эндпоинты
|
||||||
|
- name: process
|
||||||
|
description: Процессы согласования/подписи
|
||||||
|
- name: sign
|
||||||
|
description: Отправка подписей в процесс
|
||||||
|
- name: operate
|
||||||
|
description: Управление определениями процессов (BPMN)
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/:
|
||||||
|
get:
|
||||||
|
tags: [infra]
|
||||||
|
summary: Проверка доступности
|
||||||
|
description: Возвращает 200 OK. Требует валидной авторизации (middleware).
|
||||||
|
operationId: root
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
"401":
|
||||||
|
description: Не авторизован
|
||||||
|
|
||||||
|
/process/:
|
||||||
|
post:
|
||||||
|
tags: [process]
|
||||||
|
summary: Запустить процесс
|
||||||
|
description: |
|
||||||
|
Создаёт инстанс процесса согласования/подписи в Camunda по документам
|
||||||
|
из `payload`. Если для `flow_id` уже есть активный инстанс — вернётся
|
||||||
|
`400`.
|
||||||
|
operationId: createProcess
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ProcessCreateRequest"
|
||||||
|
responses:
|
||||||
|
"202":
|
||||||
|
description: Процесс принят к обработке
|
||||||
|
"400":
|
||||||
|
description: Ошибка валидации или активный инстанс уже существует
|
||||||
|
"401":
|
||||||
|
description: Не авторизован
|
||||||
|
"500":
|
||||||
|
description: Внутренняя ошибка (ошибка создания инстанса в Camunda)
|
||||||
|
|
||||||
|
/process/{instance_key}:
|
||||||
|
get:
|
||||||
|
tags: [process]
|
||||||
|
summary: Получить состояние процесса
|
||||||
|
description: |
|
||||||
|
Возвращает текущее состояние процесса по `flow_id` (в пути — числовой
|
||||||
|
ключ). Логика: если запись есть в кеше, но нет активного инстанса в
|
||||||
|
Camunda — процесс ещё обрабатывается (`425`).
|
||||||
|
operationId: getProcess
|
||||||
|
parameters:
|
||||||
|
- name: instance_key
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: Числовой идентификатор (`flow_id`)
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Состояние процесса
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/GetProcessInstanceResponse"
|
||||||
|
"401":
|
||||||
|
description: Не авторизован
|
||||||
|
"404":
|
||||||
|
description: Процесс не найден
|
||||||
|
"425":
|
||||||
|
description: Too Early — процесс ещё обрабатывается
|
||||||
|
"500":
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/sign/:
|
||||||
|
post:
|
||||||
|
tags: [sign]
|
||||||
|
summary: Отправить подписи в процесс
|
||||||
|
description: |
|
||||||
|
Публикует сообщение `signRequest` в процесс Camunda с подписями из
|
||||||
|
`payload`. Для элементов с `mrpa_id` предварительно проверяется доступ
|
||||||
|
через sarex-backend.
|
||||||
|
operationId: sign
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/SignRequest"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Подписи приняты, сообщение отправлено в процесс
|
||||||
|
"401":
|
||||||
|
description: Отсутствует токен авторизации
|
||||||
|
"403":
|
||||||
|
description: Нет прав на MRPA
|
||||||
|
"422":
|
||||||
|
description: MRPA не найдена
|
||||||
|
"500":
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/operate/processes:
|
||||||
|
post:
|
||||||
|
tags: [operate]
|
||||||
|
summary: Загрузить определение процесса (BPMN)
|
||||||
|
description: |
|
||||||
|
Принимает BPMN-файл (multipart, поле `definition`) и деплоит его в
|
||||||
|
Zeebe.
|
||||||
|
operationId: deployProcessDefinition
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required: [definition]
|
||||||
|
properties:
|
||||||
|
definition:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
description: BPMN-файл определения процесса
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Определение загружено
|
||||||
|
"400":
|
||||||
|
description: Файл не передан/некорректен
|
||||||
|
"401":
|
||||||
|
description: Не авторизован
|
||||||
|
"500":
|
||||||
|
description: Внутренняя ошибка деплоя
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
description: |
|
||||||
|
JWT Sarex (проверяется по `PUBLIC_KEY`). Для режима Zitadel
|
||||||
|
дополнительно передаётся заголовок `Identity: Bearer <jwt>`.
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
ProcessCreateRequest:
|
||||||
|
type: object
|
||||||
|
required: [flow_id, author_id]
|
||||||
|
description: Запрос на старт процесса (`internal/dto/http.go`).
|
||||||
|
properties:
|
||||||
|
flow_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
description: Внешний ID процесса (обязателен, != 0)
|
||||||
|
author_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
description: ID автора запроса (обязателен, != 0)
|
||||||
|
company_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
step_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
payload:
|
||||||
|
type: array
|
||||||
|
description: Документы для обработки (произвольные объекты)
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
overwrite_marks:
|
||||||
|
type: boolean
|
||||||
|
mode:
|
||||||
|
type: string
|
||||||
|
description: Режим работы ("original", "copy", "both")
|
||||||
|
use_signature:
|
||||||
|
type: boolean
|
||||||
|
create_copy_on_finish:
|
||||||
|
type: boolean
|
||||||
|
comment:
|
||||||
|
type: string
|
||||||
|
is_last_signer:
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
GetProcessInstanceResponse:
|
||||||
|
type: object
|
||||||
|
description: Состояние инстанса процесса (`internal/dto/http.go`).
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
use_signature:
|
||||||
|
type: boolean
|
||||||
|
is_finished:
|
||||||
|
type: boolean
|
||||||
|
is_ready_for_sign:
|
||||||
|
type: boolean
|
||||||
|
is_last_signer:
|
||||||
|
type: boolean
|
||||||
|
create_copy_on_finish:
|
||||||
|
type: boolean
|
||||||
|
comment:
|
||||||
|
type: string
|
||||||
|
payload:
|
||||||
|
description: Полезная нагрузка процесса (структура зависит от процесса)
|
||||||
|
nullable: true
|
||||||
|
instance_key:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
|
||||||
|
SignRequest:
|
||||||
|
type: object
|
||||||
|
required: [flow_id, payload]
|
||||||
|
description: Запрос на подпись документов в процессе (`internal/dto/http.go`).
|
||||||
|
properties:
|
||||||
|
flow_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
payload:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/SignRequestPayloadElem"
|
||||||
|
|
||||||
|
SignRequestPayloadElem:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
bundle_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
author_id:
|
||||||
|
type: integer
|
||||||
|
format: uint64
|
||||||
|
signature:
|
||||||
|
type: string
|
||||||
|
description: Сгенерированная подпись (помещается в p7s)
|
||||||
|
algorithm:
|
||||||
|
type: string
|
||||||
|
mrpa_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
description: Если задан — проверяется доступ через sarex-backend
|
||||||
54
apps/comparisons/.env.example
Normal file
54
apps/comparisons/.env.example
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Пример переменных окружения для comparisons-backend.
|
||||||
|
# Значения разбираются пакетом kelseyhightower/envconfig (config/config.go, FromEnv).
|
||||||
|
# Префикса нет — имена переменных используются как есть.
|
||||||
|
# Приложение НЕ загружает .env автоматически: переменные нужно экспортировать
|
||||||
|
# в окружение (для локального запуска см. .docker/.env и docker-compose).
|
||||||
|
|
||||||
|
# API
|
||||||
|
API_ADDRESS=0.0.0.0:8080
|
||||||
|
|
||||||
|
# Database (PostgreSQL)
|
||||||
|
POSTGRES_ADDRESS=127.0.0.1
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
POSTGRES_DB=comparisons
|
||||||
|
POSTGRES_POOL_SIZE=10
|
||||||
|
# TLS-подключение к БД. При ENABLE_SSL=1 используется YC-PG-CERTIFICATE как CA.
|
||||||
|
ENABLE_SSL=0
|
||||||
|
DB_CERT_PATH=/home/user/.postgresql/root.crt
|
||||||
|
YC-PG-CERTIFICATE=
|
||||||
|
|
||||||
|
# Внешние сервисы (внутрикластерные адреса)
|
||||||
|
DOCUMENTATION_URL=http://documentations-service.documentations-stage/
|
||||||
|
EXTERNAL_DOCUMENTATION_URL=https://stage-api.sarex.io/documentations
|
||||||
|
# Хранилище файлов PDM (multistorage, config/storage.go). Без него сервис
|
||||||
|
# стартует, но с предупреждением — PDM-хранилище будет недоступно.
|
||||||
|
DOCUMENTATION_FILESTREAM_URL=http://documentations-filestream-service.documentations-stage/
|
||||||
|
WORKFLOW_URL=http://workflows-service.processing-stage/
|
||||||
|
WORKSPACE_URL=http://workspaces-service.workspaces-stage/
|
||||||
|
EXTERNAL_WORKSPACE_URL=
|
||||||
|
COMPARISON_URL=http://comparisons-backend-service.comparisons-stage/
|
||||||
|
WORKFLOW_IMAGES_VERSION=develop
|
||||||
|
BIM_V2_INTERNAL_URL=http://bim-backend-v2-service.bim-api-stage/
|
||||||
|
# Используется клиентом workflow (pdf2pdf) как django_host в параметрах задачи.
|
||||||
|
DJANGO_HOST=https://stage.sarex.io
|
||||||
|
|
||||||
|
# Comparisons
|
||||||
|
# Ограничение параллелизма ABAP-сравнения (0 — без ограничения).
|
||||||
|
ABAP_FIXED_CONC=0
|
||||||
|
|
||||||
|
# Sentry / окружение
|
||||||
|
ENVIRONMENT=stage
|
||||||
|
SENTRY_DSN=
|
||||||
|
SENTRY_DEBUG=0
|
||||||
|
|
||||||
|
# Отладка
|
||||||
|
# Логировать SQL-запросы (go-pg query hook).
|
||||||
|
ENABLE_SQL_QUERY=0
|
||||||
|
|
||||||
|
# --- Дополнительно (используются только при локальном запуске / внешними
|
||||||
|
# --- библиотеками, кодом config.Config напрямую не читаются) ---
|
||||||
|
# S3_SERVICE_ACCOUNT=/etc/sarex/yc_s3_doc_account.json
|
||||||
|
# DJANGO_ORIGINATOR=docs_local
|
||||||
|
# NAMESPACE=local
|
||||||
148
apps/comparisons/CONFIGURATION.md
Normal file
148
apps/comparisons/CONFIGURATION.md
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# Конфигурация проекта comparisons-backend
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса `comparisons-backend` (Go).
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` функцией `FromEnv()` через библиотеку [`kelseyhightower/envconfig`](https://github.com/kelseyhightower/envconfig) (структура `Config`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **префикса нет** — `envconfig.Process("", &cfg)` вызывается с пустым префиксом, поэтому имена переменных совпадают с тегами `envconfig:"..."` (напр. `POSTGRES_ADDRESS`, `API_ADDRESS`);
|
||||||
|
- **вложенности нет** — все переменные плоские (без разделителя секций);
|
||||||
|
- отдельные значения читаются напрямую через `os.Getenv` в обход структуры `Config`: `DOCUMENTATION_FILESTREAM_URL` (`config/storage.go`) и `DJANGO_HOST` (`clients/workflow_cli/pdf2pdf.go`).
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет. Приложение **не загружает `.env` автоматически** — переменные нужно экспортировать в окружение самому либо задавать через `--env`/`env_file` (docker-compose).
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса. Собирается через `make api` (`go install ./cmd/api`, `./cmd/migrations`) |
|
||||||
|
| Локально (контейнеры) | `.docker/.env` + `.docker/docker-compose.yml` (`make docker`). Postgres поднимается из этого же compose |
|
||||||
|
| Kubernetes (Helm, репозиторий бэкенда) | `.helm/values-<env>.yaml`: блок `envs` (обычные значения) и `secrets` (значения из k8s-секретов); шаблон `.helm/templates/api.yaml` |
|
||||||
|
| Kubernetes (Kustomize, этот репозиторий infra) | `apps/comparisons/base` + оверлеи; env задаются в `base/backend-deployment.yaml` и патчах оверлеев. **Схема переменных здесь отличается — см. раздел ниже** |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: подключает шаблоны `generic/common-ci` (stage/preprod/prod), job `unit-tests` (образ `golang:1.21`) |
|
||||||
|
|
||||||
|
Способы запуска процессов (`cmd/*`):
|
||||||
|
|
||||||
|
| Команда | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `api` (`make api`) | `cmd/api/main.go` | HTTP API (gorilla/mux). Перед стартом настраивает Sentry и подключение к Postgres |
|
||||||
|
| `migrations` | `cmd/migrations/main.go` | Миграции БД (`robinjoseph08/go-pg-migrations`). Создание: `go run ./cmd/migrations/main.go create <name>` |
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что явного значения по умолчанию нет (пустая строка / нулевое значение типа Go). Обязательность отдельных переменных проверяется в рантайме при создании клиентов.
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `API_ADDRESS` | string | — | Адрес и порт прослушивания HTTP-сервера (напр. `0.0.0.0:8080`) |
|
||||||
|
|
||||||
|
### Database (PostgreSQL)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS` | string | — | Хост PostgreSQL |
|
||||||
|
| `POSTGRES_PORT` | string | — | Порт PostgreSQL |
|
||||||
|
| `POSTGRES_USER` | string | — | Пользователь БД |
|
||||||
|
| `POSTGRES_PASSWORD` | string | — | Пароль пользователя БД |
|
||||||
|
| `POSTGRES_DB` | string | — | Имя базы данных |
|
||||||
|
| `POSTGRES_POOL_SIZE` | int | — | Размер пула соединений (в коде `main.go` пул жёстко равен `30`; переменная читается, но фактически используется значение из кода) |
|
||||||
|
| `ENABLE_SSL` | bool | `false` | Подключение к БД по TLS. При `true` используется `YC-PG-CERTIFICATE` как корневой сертификат, `ServerName` = `POSTGRES_ADDRESS` |
|
||||||
|
| `DB_CERT_PATH` | string | — | Путь к CA-сертификату PostgreSQL (используется инфраструктурой; в Helm — смонтированный файл) |
|
||||||
|
| `YC-PG-CERTIFICATE` | string | — | Содержимое CA-сертификата для TLS-подключения к БД. Имя с дефисами не соответствует остальным (`envconfig` допускает произвольный тег) |
|
||||||
|
|
||||||
|
### Внешние сервисы
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DOCUMENTATION_URL` | string | — | Внутренний URL сервиса документаций (обязателен для `documentation_cli`) |
|
||||||
|
| `EXTERNAL_DOCUMENTATION_URL` | string | — | Внешний URL сервиса документаций (обязателен для `documentation_cli`) |
|
||||||
|
| `DOCUMENTATION_FILESTREAM_URL` | string | — | URL PDM-хранилища файлов (`config/storage.go`). Если не задан — сервис стартует, но PDM-хранилище недоступно (лог-warning) |
|
||||||
|
| `WORKFLOW_URL` | string | — | Внутренний URL сервиса workflow (обязателен для `workflow_cli`) |
|
||||||
|
| `WORKSPACE_URL` | string | — | Внутренний URL сервиса workspace (обязателен для `workspace_cli`) |
|
||||||
|
| `EXTERNAL_WORKSPACE_URL` | string | — | Внешний URL сервиса workspace |
|
||||||
|
| `COMPARISON_URL` | string | — | URL самого сервиса сравнений (обязателен для `workflow_cli`) |
|
||||||
|
| `WORKFLOW_IMAGES_VERSION` | string | — | Версия/тег образов задач workflow (обязателен для `workflow_cli`) |
|
||||||
|
| `BIM_V2_INTERNAL_URL` | string | — | Внутренний URL BIM API v2 |
|
||||||
|
| `DJANGO_HOST` | string | — | Хост Django (LK). Передаётся как `django_host` в параметры задачи pdf2pdf (`clients/workflow_cli/pdf2pdf.go`) |
|
||||||
|
|
||||||
|
### Comparisons
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ABAP_FIXED_CONC` | uint64 | `0` | Ограничение параллелизма ABAP-сравнения (`0` — без ограничения) |
|
||||||
|
|
||||||
|
### Sentry и окружение
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENVIRONMENT` | string | — | Окружение развёртывания (`stage`/`preprod`/`prod`); передаётся в Sentry |
|
||||||
|
| `SENTRY_DSN` | string | — | DSN Sentry |
|
||||||
|
| `SENTRY_DEBUG` | bool | `false` | Debug-режим Sentry |
|
||||||
|
|
||||||
|
### Отладка
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_SQL_QUERY` | bool | `false` | Логировать SQL-запросы (query hook go-pg) |
|
||||||
|
|
||||||
|
### Дополнительные переменные (не читаются `config.Config`)
|
||||||
|
|
||||||
|
Присутствуют в `.docker/.env` для локального запуска или используются внешними библиотеками (`gotools`), но напрямую структурой `Config` не разбираются:
|
||||||
|
|
||||||
|
| Переменная | Где встречается | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `S3_SERVICE_ACCOUNT` | `.docker/.env` | Путь к JSON сервисного аккаунта S3 |
|
||||||
|
| `DJANGO_ORIGINATOR` | `.docker/.env` | Ориджинатор для интеграции с Django |
|
||||||
|
| `NAMESPACE` | `.docker/.env` | Логическое пространство имён для локального запуска |
|
||||||
|
| `API_ADDRESS_FILE` | `.helm/values-*.yaml` | Адрес file-варианта API (задан в Helm, кодом не используется) |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values-<env>.yaml`)
|
||||||
|
|
||||||
|
Обычные значения задаются в блоке `envs` для каждого окружения (`stage`/`preprod`/`production`) и содержат те же переменные приложения, что описаны выше (различаются адресами БД/сервисов, `WORKFLOW_IMAGES_VERSION`, доменом Django и т.п.).
|
||||||
|
|
||||||
|
Значения из секретов (блок `secrets`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secret_name`) | Ключ (`secret_key`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_USER` | `ya-pg-secret` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD` | `ya-pg-secret` | `password` |
|
||||||
|
| `YC-PG-CERTIFICATE` | `yc-pg-certificate` | `certificate` |
|
||||||
|
|
||||||
|
Прочие значения чарта (не переменные приложения): `api.*` (имя, образ, порт, реплики, ресурсы, ingress `api_host`/`api_host_prefix`/`api_path`/`internal_path`, `permitted_ns`), `version`, `imagePullSecrets`.
|
||||||
|
|
||||||
|
## Развёртывание через Kustomize (этот репозиторий, `apps/comparisons`)
|
||||||
|
|
||||||
|
Структура: `base` (общие манифесты) и оверлеи `brusnika-stage`, `brusnika-prod`, `yc-k8s-test`. Секреты БД и публичный JWT-ключ подтягиваются из **HashiCorp Vault** (аннотации `vault.hashicorp.com/*` в `base/backend-deployment.yaml`), а не из k8s-секретов.
|
||||||
|
|
||||||
|
> **Важно: расхождение схем переменных.** Манифест `base/backend-deployment.yaml` использует другой (более новый) набор имён переменных, чем Go-код из `comparisons-backend` (`config/config.go`): напр. `HTTP_PORT`, `LOGGER_LOG_LEVEL`, `DATABASE_NAME`, `DOCUMENTATIONS_INTERNAL_HOST`, `DOCUMENTATIONS_EXTERNAL_HOST`, `WORKFLOWS_HOST`, `WORKFLOWS_DJANGO_HOST`, `WORKFLOWS_BIMV2_INTERNAL_HOST`, `WORKSPACES_HOST`, `EAV_HOST`, `APP_NAME`, `AUTH_PUBLIC_KEY`, `WORKFLOWS_CONFIG_FILEPATH` и др., а также `/ping` в health-проверках и образ `comparisons_backend_prod`. Такой схемы нет в Go-репозитории. Перед использованием этих файлов стоит убедиться, какой именно образ бэкенда деплоится оверлеем: если это Go-сервис из `comparisons-backend`, набор env нужно привести к именам из таблиц выше.
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны `generic/common-ci` и переключает окружение по ветке/тегу:
|
||||||
|
|
||||||
|
| Условие | Шаблон | Окружение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ветка `stage` | `gitlab-ci/comparisons-backend/.gitlab-ci-stage.yml` | stage |
|
||||||
|
| ветка `master` | `gitlab-ci/comparisons-backend/.gitlab-ci-preprod.yml` | preprod |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `gitlab-ci/comparisons-backend/.gitlab-ci-prod.yml` | prod |
|
||||||
|
|
||||||
|
Стадии: `prebuild-secscan`, `dependencies-build`, `unittest`, `build`, `state-update`, `deploy`. Job `unit-tests` (образ `golang:1.21`) выполняет `make unit-tests`.
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Postgres поднимается через `.docker/docker-compose.yml` (`make docker`). Минимально необходимо задать:
|
||||||
|
|
||||||
|
- `API_ADDRESS`
|
||||||
|
- `POSTGRES_ADDRESS`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_POOL_SIZE`, `ENABLE_SSL` (`0` локально)
|
||||||
|
- `DOCUMENTATION_URL`, `EXTERNAL_DOCUMENTATION_URL`, `DOCUMENTATION_FILESTREAM_URL`
|
||||||
|
- `WORKFLOW_URL`, `WORKSPACE_URL`, `COMPARISON_URL`, `WORKFLOW_IMAGES_VERSION`
|
||||||
|
- `BIM_V2_INTERNAL_URL`, `DJANGO_HOST`
|
||||||
|
- `ENVIRONMENT`; при использовании Sentry — `SENTRY_DSN`
|
||||||
|
- по желанию: `ENABLE_SQL_QUERY` (`1` для отладки SQL), `ABAP_FIXED_CONC`
|
||||||
|
|
||||||
|
Готовые значения-примеры приведены в `.env.example`.
|
||||||
77
apps/comparisons/ENDPOINTS.md
Normal file
77
apps/comparisons/ENDPOINTS.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует comparisons-frontend
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается модуль (микрофронтенд `comparisons-frontend`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Все запросы описаны декларативно в реестре `module/networking/endpoints.ts` (объект `endpoints`). Каждый эндпоинт задаётся структурой `Endpoint`:
|
||||||
|
|
||||||
|
- `service` — логическое имя сервиса (`EServices`, см. таблицу хостов ниже);
|
||||||
|
- `method` — HTTP-метод (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`);
|
||||||
|
- `path(args)` — функция, возвращающая путь запроса (с подстановкой параметров/query);
|
||||||
|
- `body(args)` — опционально, формирование тела запроса.
|
||||||
|
|
||||||
|
Запрос выполняется единой функцией `fetch(endpoint, params)`, которая через `httpService` (`module/httpService/httpService.ts`, поверх `@sarex-team/sdk-js` + `axios`) отправляет запрос на базовый хост сервиса. Базовый хост подставляется `resolveHost(service)` из `module/httpService/hosts.ts` в зависимости от `buildEnv` (`__BUILD_ENV__`, задаётся сборкой; по умолчанию `prod`). Результат возвращается как `{ resp }` либо `{ errMessage }`.
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значения из `module/httpService/hosts.ts` (`allHosts`). Итоговый URL = `<базовый хост сервиса>` + `path` эндпоинта. Хосты берутся из `@sarex-team/sdk-js` (`resolveHost`).
|
||||||
|
|
||||||
|
| Сервис (`EServices`) | Назначение | `stage` | `prod` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `comparisons` | Сервис сравнений (comparisons-backend) | `https://stage-api.sarex.io/comparisons` | `https://api.sarex.io/comparisons` |
|
||||||
|
| `documentations` | Сервис документации (диски, документы) | `https://stage-api.sarex.io/documentations` | `https://api.sarex.io/documentations` |
|
||||||
|
| `sarexApi` | Gateway/API Sarex (`/gateway`) | `https://stage-api.sarex.io` | `https://api.sarex.io` |
|
||||||
|
| `sarex` | Локальный сервис данных (`/api/core`) | `""` (относительные пути) | `""` |
|
||||||
|
| `workflows` | Сервис обработки документов | `https://stage-api.sarex.io/workflows` | `https://api.sarex.io/workflows` |
|
||||||
|
| `bimv2` | BIM API v2 | `https://stage-api.sarex.io/bimv2` | `https://api.sarex.io/bimv2` |
|
||||||
|
| `zitadel` | IdP (аутентификация) | `https://idp.dev.stage.sarex.io` | `https://login.sarex.io` |
|
||||||
|
|
||||||
|
> Также определены окружения `local`, `preprod` и `contour`. В `local` сервисы проксируются на `https://localhost:9000/sarex-backend` и `https://localhost:9000/sarex-api-backend/*`. В `contour` используются относительные пути (`/comparisons`, `/documentations`, `/bimv2`, `/workflows`) для изолированного контура. В `preprod` — `https://api.preprod.sarex.io/*`.
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `comparisons` — Сервис сравнений
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getTypes` | GET | `/api/v1/types` | Справочник типов сравнений и их параметров |
|
||||||
|
| `createComparison` | POST | `/api/v1/comparisons` | Создать сравнение (тело — параметры сравнения) |
|
||||||
|
| `getComparisons` | GET | `/api/v1/comparisons?workspace_id={workspaceId}` | Список сравнений рабочей области |
|
||||||
|
| `deleteComparison` | DELETE | `/api/v1/comparisons/{id}` | Удалить сравнение |
|
||||||
|
|
||||||
|
### `documentations` — Сервис документации
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getDisks` | GET | `/api/v1/disks` | Список дисков |
|
||||||
|
| `getDocuments` | GET | `/api/v1/disks/{id}/documents` | Документы диска |
|
||||||
|
| `getNearestNameTemplate` | GET | `/api/v1/documents/{documentId}/name_template` | Ближайший шаблон имени документа |
|
||||||
|
|
||||||
|
### `sarexApi` — Gateway/API Sarex
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getDocumentsV2` | GET | `/gateway/api/v1/disks/{id}/documents?parent_id=&child_id=&search=&{attributeValue}` | Документы диска с фильтрами (родитель/ребёнок/поиск/атрибуты) |
|
||||||
|
|
||||||
|
### `sarex` — Локальный сервис данных
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getUsersByIDs` | GET | `/api/core/users/?id={ids}` | Пользователи по списку id |
|
||||||
|
|
||||||
|
### `workflows` — Сервис обработки документов
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getWorkflow` | GET | `/api/v1/workflows/{workflowId}` | Workflow по id |
|
||||||
|
|
||||||
|
### `bimv2` — BIM API v2
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getBim` | GET | `/api/v1/bims/{bimId}` | BIM-модель по id |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
`fetch` перехватывает исключения запроса и возвращает `{ errMessage }` (строка ошибки) вместо данных; успешный ответ приходит как `{ resp: <data> }`. Явного маппинга кодов ответа в реестре нет — обработка и отображение ошибок выполняются на уровне репозиториев/вью-моделей, использующих `fetch`.
|
||||||
793
apps/comparisons/openapi.yaml
Normal file
793
apps/comparisons/openapi.yaml
Normal file
@ -0,0 +1,793 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
|
||||||
|
info:
|
||||||
|
title: Comparisons Service API
|
||||||
|
version: "1.0.0"
|
||||||
|
description: |
|
||||||
|
REST API сервиса **comparisons-backend** (`pdm/comparisons-backend`) — создание и
|
||||||
|
просмотр сравнений (облако-BIM отклонение/статусы, облако-облако, облако-поверхность,
|
||||||
|
pdf-pdf), их элементов, изменений и фильтров.
|
||||||
|
|
||||||
|
Сервис написан на Go (роутер **gorilla/mux**). Сервер собирается функцией
|
||||||
|
`bootstrapAPI` в `cmd/api/bootstrap.go`. Роутинг состоит из двух групп:
|
||||||
|
|
||||||
|
- публичный API — префикс `/api/v1` (`cmd/api/routes_api.go`);
|
||||||
|
- внутренний API — префикс `/internal/v1` (`cmd/api/routes_internal.go`),
|
||||||
|
предназначен для вызовов внутри кластера (через ingress не публикуется).
|
||||||
|
|
||||||
|
### Аутентификация
|
||||||
|
Публичные эндпоинты (`/api/v1/*`) требуют JWT: middleware `auth.JWTToCtx` +
|
||||||
|
`auth.JWTUserExtractorFromCtx` (`gitlab.com/sarex-team/gotools/auth`). Токен
|
||||||
|
передаётся заголовком `Authorization: Bearer <jwt>`; middleware
|
||||||
|
`auth.DeleteJWTFromQueryMiddleware` дополнительно позволяет передать токен
|
||||||
|
query-параметром `jwt` (он удаляется из запроса после разбора).
|
||||||
|
|
||||||
|
Внутренние эндпоинты (`/internal/v1/*`) аутентификации на уровне приложения
|
||||||
|
не требуют — ограничение доступа обеспечивается сетевым слоем.
|
||||||
|
|
||||||
|
### Обработка ошибок
|
||||||
|
Ошибки возвращаются функцией `httperror.Write` (`gotools/httperror`). Основные
|
||||||
|
статусы: `400` — некорректный запрос/параметры, `404` — сравнение не найдено,
|
||||||
|
`500` — внутренняя ошибка. Идентификатор запроса пробрасывается middleware
|
||||||
|
`reqid`.
|
||||||
|
|
||||||
|
### Замечания (расхождения кода)
|
||||||
|
- `GET /api/v1/comparisons` возвращает данные разной формы в зависимости от
|
||||||
|
query-параметра: `workspace_id` → `{ comparisons: [] }`, `document_id` →
|
||||||
|
`{ results: [] }`, `bundle_id` → одиночный объект сравнения.
|
||||||
|
- `GET /api/v1/filter_fields` и `GET /api/v1/elements` работают только со
|
||||||
|
сравнениями типа `deviation`; для других типов вернётся `400`.
|
||||||
|
- Схема сравнения полиморфна по полю `type` (`deviation`/`c2c`/`c2s`/`abap`/`pdf2pdf`).
|
||||||
|
|
||||||
|
contact:
|
||||||
|
name: comparisons-backend
|
||||||
|
url: https://gitlab.com/sarex-team/comparisons-backend
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://api.sarex.io/comparisons
|
||||||
|
description: Production (ingress)
|
||||||
|
- url: https://stage-api.sarex.io/comparisons
|
||||||
|
description: Stage (ingress)
|
||||||
|
- url: https://api.preprod.sarex.io/comparisons
|
||||||
|
description: Preprod (ingress)
|
||||||
|
- url: http://comparisons-backend-service.comparisons-stage
|
||||||
|
description: Внутрикластерный адрес (ClusterIP). Единственный способ достучаться до /internal/v1
|
||||||
|
- url: http://localhost:8080
|
||||||
|
description: Локальный запуск (API_ADDRESS по умолчанию 0.0.0.0:8080)
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: types
|
||||||
|
description: Справочник типов сравнений
|
||||||
|
- name: comparisons
|
||||||
|
description: Сравнения — создание, просмотр, удаление
|
||||||
|
- name: elements
|
||||||
|
description: Элементы сравнения и их изменения
|
||||||
|
- name: internal
|
||||||
|
description: Внутренние эндпоинты (только внутри кластера)
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
|
||||||
|
paths:
|
||||||
|
# ==========================================================================
|
||||||
|
# Types
|
||||||
|
# ==========================================================================
|
||||||
|
/api/v1/types:
|
||||||
|
get:
|
||||||
|
tags: [types]
|
||||||
|
summary: Справочник типов сравнений
|
||||||
|
description: Возвращает доступные типы сравнений и их параметры (для построения форм).
|
||||||
|
operationId: getTypes
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Список типов сравнений
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
types:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/CompareType'
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# Comparisons
|
||||||
|
# ==========================================================================
|
||||||
|
/api/v1/comparisons:
|
||||||
|
post:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Создать сравнение
|
||||||
|
description: |
|
||||||
|
Создаёт сравнение указанного типа. Автор берётся из JWT. Если `parent_doc_id`
|
||||||
|
и `parent_disk_id` не заданы — вычисляются по рабочей области.
|
||||||
|
operationId: createComparison
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Созданное сравнение
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Comparison'
|
||||||
|
'400':
|
||||||
|
description: Некорректный запрос / неизвестный тип сравнения
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
get:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Список сравнений
|
||||||
|
description: |
|
||||||
|
Возвращает сравнения по одному из query-параметров. Форма ответа зависит
|
||||||
|
от параметра (см. описание в `info`). Должен быть задан ровно один параметр.
|
||||||
|
operationId: getComparisons
|
||||||
|
parameters:
|
||||||
|
- name: workspace_id
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: "Сравнения рабочей области → ответ `{ comparisons: [] }`"
|
||||||
|
- name: bundle_id
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: Сравнение по бандлу → ответ — одиночный объект
|
||||||
|
- name: document_id
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: "Сравнения документа → ответ `{ results: [] }`"
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Результат (форма зависит от параметра запроса)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
oneOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
comparisons:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Comparison'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Comparison'
|
||||||
|
- $ref: '#/components/schemas/Comparison'
|
||||||
|
'400':
|
||||||
|
description: Не задан workspace_id / bundle_id / document_id
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/api/v1/comparisons/{comparison_id}:
|
||||||
|
parameters:
|
||||||
|
- name: comparison_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
get:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Сравнение по id
|
||||||
|
operationId: getComparisonById
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Сравнение
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Comparison'
|
||||||
|
'404':
|
||||||
|
description: Сравнение не найдено
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
delete:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Удалить сравнение
|
||||||
|
description: Мягкое удаление сравнения. Автор берётся из JWT.
|
||||||
|
operationId: deleteComparisonById
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Успешно удалено
|
||||||
|
'400':
|
||||||
|
description: Некорректный id
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/api/v1/filter_fields:
|
||||||
|
get:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Поля фильтрации сравнения
|
||||||
|
description: |
|
||||||
|
Возвращает набор полей фильтрации для сравнения типа `deviation` по документу.
|
||||||
|
Для полей отклонения (`deviation`, `deviation_x/y/z`) заполняются `min`/`max`.
|
||||||
|
Если сравнение по документу не найдено — вернётся пустой `results`.
|
||||||
|
operationId: getFilterFields
|
||||||
|
parameters:
|
||||||
|
- name: doc_id
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Поля фильтрации
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/FilterFields'
|
||||||
|
'400':
|
||||||
|
description: Не задан doc_id / сравнение не типа deviation
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/api/v1/tolerance:
|
||||||
|
get:
|
||||||
|
tags: [comparisons]
|
||||||
|
summary: Допуск (tolerance) сравнения
|
||||||
|
description: Возвращает значение допустимого отклонения для сравнения типа `deviation` по бандлу.
|
||||||
|
operationId: getTolerance
|
||||||
|
parameters:
|
||||||
|
- name: bundle_id
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Допуск
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tolerance:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
'400':
|
||||||
|
description: Не задан / некорректный bundle_id
|
||||||
|
'404':
|
||||||
|
description: Сравнение не найдено
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# Elements
|
||||||
|
# ==========================================================================
|
||||||
|
/api/v1/elements:
|
||||||
|
get:
|
||||||
|
tags: [elements]
|
||||||
|
summary: Список элементов сравнения
|
||||||
|
description: |
|
||||||
|
Постранично возвращает элементы сравнения типа `deviation` по документу.
|
||||||
|
Параметры отклонения задаются строкой вида `min:max`. Если сравнение не
|
||||||
|
найдено — вернётся пустой `results` с типами по умолчанию.
|
||||||
|
operationId: getListElements
|
||||||
|
parameters:
|
||||||
|
- name: doc_id
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 10
|
||||||
|
minimum: 0
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
minimum: 0
|
||||||
|
- name: order_by
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [deviation, deviation_x, deviation_y, deviation_z, sarex_id, name]
|
||||||
|
default: sarex_id
|
||||||
|
- name: order
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [asc, desc]
|
||||||
|
default: asc
|
||||||
|
- name: deviation
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Диапазон общего отклонения в формате `min:max`
|
||||||
|
- name: deviation_x
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Диапазон отклонения по X в формате `min:max`
|
||||||
|
- name: deviation_y
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Диапазон отклонения по Y в формате `min:max`
|
||||||
|
- name: deviation_z
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Диапазон отклонения по Z в формате `min:max`
|
||||||
|
- name: deviation_status
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [in_tolerance, out_of_tolerance]
|
||||||
|
- name: view_status
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [viewed, not_viewed]
|
||||||
|
- name: only_with_comment
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Страница элементов
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ListElementsResponse'
|
||||||
|
'400':
|
||||||
|
description: Некорректные параметры запроса
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/api/v1/elements/{element_id}:
|
||||||
|
parameters:
|
||||||
|
- name: element_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
patch:
|
||||||
|
tags: [elements]
|
||||||
|
summary: Обновить элемент
|
||||||
|
description: |
|
||||||
|
Обновляет один из атрибутов элемента: комментарий, статус отклонения или
|
||||||
|
статус просмотра. Должно быть задано ровно одно из полей. Изменение
|
||||||
|
фиксируется записью в истории изменений.
|
||||||
|
operationId: updateElement
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UpdateElementRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Успешно обновлено (тело не возвращается)
|
||||||
|
'400':
|
||||||
|
description: Некорректный запрос
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
/api/v1/elements/{element_id}/changes:
|
||||||
|
get:
|
||||||
|
tags: [elements]
|
||||||
|
summary: История изменений элемента
|
||||||
|
operationId: getElementChanges
|
||||||
|
parameters:
|
||||||
|
- name: element_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Список изменений
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
changes:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Change'
|
||||||
|
'400':
|
||||||
|
description: Некорректный element_id
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# Internal
|
||||||
|
# ==========================================================================
|
||||||
|
/internal/v1/comparisons/{comparison_id}/webhook:
|
||||||
|
post:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Webhook результата сравнения (внутренний)
|
||||||
|
description: |
|
||||||
|
Вызывается после завершения обработки сравнения типа `deviation`. Скачивает
|
||||||
|
`deviation_json` из PDM-хранилища по бандлу, создаёт элементы сравнения и
|
||||||
|
возвращает содержимое `deviation_json`. Аутентификация на уровне приложения
|
||||||
|
не требуется.
|
||||||
|
operationId: comparisonWebhook
|
||||||
|
security: []
|
||||||
|
parameters:
|
||||||
|
- name: comparison_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Содержимое deviation_json
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DeviationJSON'
|
||||||
|
'404':
|
||||||
|
description: Сравнение не найдено
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
description: |
|
||||||
|
JWT в заголовке `Authorization: Bearer <jwt>`. Допускается передача
|
||||||
|
query-параметром `jwt` (удаляется middleware после разбора).
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
ComparisonType:
|
||||||
|
type: string
|
||||||
|
enum: [c2c, c2s, deviation, abap, pdf2pdf]
|
||||||
|
|
||||||
|
CompareType:
|
||||||
|
type: object
|
||||||
|
description: Тип сравнения и набор его параметров (для формы создания).
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
$ref: '#/components/schemas/ComparisonType'
|
||||||
|
verbose_name:
|
||||||
|
type: string
|
||||||
|
params:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/CompareFormParam'
|
||||||
|
|
||||||
|
CompareFormParam:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
verbose_name:
|
||||||
|
type: string
|
||||||
|
hint:
|
||||||
|
type: string
|
||||||
|
source:
|
||||||
|
type: string
|
||||||
|
enum: [form, viewer]
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
description: Тип поля (напр. cloud, bimv2, number, multichoice, choice, array, pdf, surface)
|
||||||
|
options:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
default: {}
|
||||||
|
min:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
max:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
|
||||||
|
CreateRequest:
|
||||||
|
type: object
|
||||||
|
required: [name, workspace_id, type, company_id, params]
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
workspace_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
parent_doc_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
nullable: true
|
||||||
|
parent_disk_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/ComparisonType'
|
||||||
|
company_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
params:
|
||||||
|
type: object
|
||||||
|
description: Параметры сравнения, зависят от type (см. /api/v1/types).
|
||||||
|
additionalProperties: true
|
||||||
|
|
||||||
|
Comparison:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
Полиморфное сравнение. Поле `type` определяет набор `params`. Ниже приведён
|
||||||
|
пример для типа `deviation`; для других типов набор params отличается.
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/ComparisonType'
|
||||||
|
created_by:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
workspace_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
workflow_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
document_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
nullable: true
|
||||||
|
bundle_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
params:
|
||||||
|
$ref: '#/components/schemas/DeviationParams'
|
||||||
|
|
||||||
|
DeviationParams:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
cloud_bundle_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
model_bundle_id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
down_sample_size:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
resolution:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
linear_margin:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
tolerance:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
sarex_ids:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
transformation:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
applied_statuses:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
crop_margin:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
Element:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
sarex_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
deviation_status:
|
||||||
|
type: string
|
||||||
|
enum: [in_tolerance, out_of_tolerance]
|
||||||
|
view_status:
|
||||||
|
type: string
|
||||||
|
enum: [viewed, not_viewed]
|
||||||
|
deviation:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
deviation_x:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
deviation_y:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
deviation_z:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
axis_and_angle:
|
||||||
|
type: object
|
||||||
|
description: Ось и угол поворота (utils.AxisAndAngle)
|
||||||
|
additionalProperties: true
|
||||||
|
comment:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
bbox_matrix:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
reg_matrix:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
|
||||||
|
ListElementsResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
next:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
previous:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
types:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Option'
|
||||||
|
crop_margin:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nullable: true
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Element'
|
||||||
|
|
||||||
|
UpdateElementRequest:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
Должно быть задано ровно одно из полей (`comment`, `deviation_status`,
|
||||||
|
`view_status`) — валидатор `required_without_all`.
|
||||||
|
properties:
|
||||||
|
comment:
|
||||||
|
type: string
|
||||||
|
deviation_status:
|
||||||
|
type: string
|
||||||
|
enum: [in_tolerance, out_of_tolerance]
|
||||||
|
view_status:
|
||||||
|
type: string
|
||||||
|
enum: [viewed, not_viewed]
|
||||||
|
|
||||||
|
Change:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
element_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
author:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
changed_field:
|
||||||
|
type: string
|
||||||
|
enum: [deviation_status, view_status, comment]
|
||||||
|
old_value:
|
||||||
|
type: string
|
||||||
|
new_value:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
FilterFields:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
enum: [deviation, deviation_x, deviation_y, deviation_z, deviation_status, view_status, only_with_comment]
|
||||||
|
verbose_name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum: [checkbox, selector, slider]
|
||||||
|
options:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Option'
|
||||||
|
min:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nullable: true
|
||||||
|
max:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
Option:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
verbose_name:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
DeviationJSON:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
crop_margin:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
nodes:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/Node'
|
||||||
|
|
||||||
|
Node:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
bbox_matrix:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
reg_matrix:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
node_name:
|
||||||
|
type: string
|
||||||
259
apps/django/.env.example
Normal file
259
apps/django/.env.example
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Пример конфигурации sarex-backend (Django) + sarex-frontend
|
||||||
|
# Значения читаются кодом через django-environ (env(...)) и pydantic-settings
|
||||||
|
# (классы *Settings с env_prefix) в config/settings/*.py.
|
||||||
|
# В кластере переменные приходят из Vault (файлы /vault/secrets/*) и из блока
|
||||||
|
# env Deployment-манифестов (см. base/backend-deployment.yaml, celery-deployment.yaml).
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Django core
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
DJANGO_SETTINGS_MODULE=config.settings.production
|
||||||
|
DJANGO_DEBUG=False
|
||||||
|
DJANGO_ISOLATED=False
|
||||||
|
ALLOWED_HOSTS='*'
|
||||||
|
APPEND_SLASH=True
|
||||||
|
FZ152_COMPLIANCE=False
|
||||||
|
PDM_SYNC=1
|
||||||
|
OBJECT_STORAGE_SYNC=True
|
||||||
|
# Секретный ключ Django. В production.py по умолчанию задан хардкодом,
|
||||||
|
# при необходимости переопределяется переменной SECRET_KEY.
|
||||||
|
SECRET_KEY=
|
||||||
|
# STATIC_ROOT / MEDIA_ROOT нужны только если раскомментированы в production.py
|
||||||
|
# STATIC_ROOT=/opt/sarex/static
|
||||||
|
# MEDIA_ROOT=/opt/sarex/media
|
||||||
|
DISK_USAGE_ROOT=/
|
||||||
|
USE_SSL_FOR_URL_SERIALIZATION=True
|
||||||
|
WEB_APP_AUTH_MODE=jwt-session-based
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# База данных (PostgreSQL) — читается в config/settings/production.py
|
||||||
|
# В кластере приходит из Vault-секрета secrets/data/postgresql/apps/django
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
DJANGO_POSTGRES_HOST=postgresql.django.svc.cluster.local
|
||||||
|
DJANGO_POSTGRES_PORTS=5432
|
||||||
|
DJANGO_POSTGRES_DATABASE=sarex_db
|
||||||
|
DJANGO_POSTGRES_USER=sarex
|
||||||
|
DJANGO_POSTGRES_PASSWORD=password
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# JWT (RS512). Ключи в кластере приходят из Vault (rsa_keys), \n экранируются.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
JWT_PRIVATE_KEY=
|
||||||
|
JWT_PUBLIC_KEY=
|
||||||
|
JWT_KID=1
|
||||||
|
DJANGO_JWT_SECRET='Froom too much love of living'
|
||||||
|
SIMPLE_JWT_ISSUER=django
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Celery — брокер (RabbitMQ) и backend результатов (Redis или Postgres)
|
||||||
|
# CELERY_RABBITMQ_* приходит из Vault-секрета secrets/data/rabbitmq/apps/django
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
CELERY_USE_POSTGRES=False
|
||||||
|
CELERY_RABBITMQ_HOST=rabbitmq.rabbitmq.svc.cluster.local
|
||||||
|
CELERY_RABBITMQ_PORT=5672
|
||||||
|
CELERY_RABBITMQ_USER=rabbit
|
||||||
|
CELERY_RABBITMQ_PASSWORD=rabbit
|
||||||
|
CELERY_RABBITMQ_VHOST=api
|
||||||
|
CELERY_REDIS_HOST=redis
|
||||||
|
CELERY_REDIS_PORT=6379
|
||||||
|
CELERY_REDIS_DATABASE=0
|
||||||
|
# Backend результатов на Postgres (используется при CELERY_USE_POSTGRES=True)
|
||||||
|
CELERY_POSTGRES_DATABASE=celery_db
|
||||||
|
CELERY_POSTGRES_USER=sarex
|
||||||
|
CELERY_POSTGRES_PASSWORD=sarex
|
||||||
|
CELERY_POSTGRES_HOST=localhost
|
||||||
|
CELERY_POSTGRES_PORT=5432
|
||||||
|
|
||||||
|
# Дублирующий набор RabbitMQ (django), прокидывается Vault-шаблоном
|
||||||
|
DJANGO_RABBIT_HOSTNAME=rabbitmq.rabbitmq.svc.cluster.local
|
||||||
|
DJANGO_RABBIT_USER=rabbit
|
||||||
|
DJANGO_RABBIT_PASS=rabbit
|
||||||
|
DJANGO_RABBIT_VHOST=api
|
||||||
|
|
||||||
|
# Redis для Django-кеша/сервисов
|
||||||
|
DJANGO_REDIS_HOST=redis
|
||||||
|
DJANGO_REDIS_PORT=6379
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Redis-кеш (CacheSettings)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
CACHE_HOST=localhost
|
||||||
|
CACHE_PORT=6379
|
||||||
|
# CACHE_PASSWORD=
|
||||||
|
CACHE_SSL=False
|
||||||
|
# CACHE_SSL_CA_CERTS=
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# S3 / объектное хранилище (S3Settings, env_prefix S3_)
|
||||||
|
# S3_* приходит из Vault-секрета secrets/data/minio/apps/django
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
S3_HOST=https://storage.yandexcloud.net
|
||||||
|
AWS_S3_ENDPOINT_URL=https://storage.yandexcloud.net
|
||||||
|
S3_LOGIN=
|
||||||
|
S3_PASSWORD=
|
||||||
|
S3_BUCKET=sarex-media-storage
|
||||||
|
S3_REGION=ru-central1
|
||||||
|
# AWS_DEFAULT_REGION=ru-central1
|
||||||
|
# Нативная библиотека загрузки в S3 (см. Dockerfile)
|
||||||
|
S3TOOLS_LIB_PATH=/opt/sarex/lib/s3tools.so
|
||||||
|
S3TOOLS_WORKERS=10
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Kafka (KafkaSettings, env_prefix KAFKA_)
|
||||||
|
# Аутентификация приходит из Vault-секрета secrets/data/kafka/apps/django
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
KAFKA_BOOTSTRAP_SERVERS='["localhost:9092"]'
|
||||||
|
KAFKA_SECURITY_PROTOCOL=
|
||||||
|
KAFKA_SASL_MECHANISM=
|
||||||
|
KAFKA_SASL_PLAIN_USERNAME=user
|
||||||
|
KAFKA_SASL_PLAIN_PASSWORD=password
|
||||||
|
KAFKA_SSL_CAFILE=/usr/local/share/ca-certificates/kafka.crt
|
||||||
|
KAFKA_TOPICS='{"planning": "message-hub-stage", "ams-sync": "ams-sync"}'
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Sentry (SentrySettings, env_prefix SENTRY_) — читается из .env.base/.env
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
SENTRY_USE=0
|
||||||
|
SENTRY_HOST="https://sentry.sarex.io/"
|
||||||
|
SENTRY_ENVIRONMENT="production"
|
||||||
|
SENTRY_TRACES_SAMPLE_RATE=1.0
|
||||||
|
SENTRY_PROFILES_SAMPLE_RATE=0.1
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Server-флаги приложения (ServerSettings, env_prefix SERVER_)
|
||||||
|
# Ниже — переменные, реально задаваемые в манифестах кластера.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
SERVER_HOST=https://lk.sarex.io
|
||||||
|
SERVER_API_HOST=https://api.sarex.io
|
||||||
|
SERVER_ZITADEL_ENABLED=True
|
||||||
|
SERVER_KAFKA_ENABLED=False
|
||||||
|
SERVER_USE_METASHAPE=0
|
||||||
|
SERVER_USE_CLICKHOUSE=0
|
||||||
|
SERVER_USE_CHANGELOG=0
|
||||||
|
SERVER_CHANGELOG_MODE=0
|
||||||
|
SERVER_CHANGELOG_MODE_SYSTEM_LOG=1
|
||||||
|
SERVER_SAVE_DIFF_DEM=1
|
||||||
|
SERVER_S3_STREAM_IMPORT=1
|
||||||
|
SERVER_USE_DJANGO_STORAGE=1
|
||||||
|
SERVER_DJANGO_URLS=1
|
||||||
|
SERVER_CHECK_IMPORT_HASH=1
|
||||||
|
SERVER_USE_WRORKFLOW_STATUS=1
|
||||||
|
SERVER_HIDE_USER_SCROLL_PERMISSIONS=0
|
||||||
|
SERVER_EXTERNAL_FIND_BY_USERNAME_ENABLED=True
|
||||||
|
SERVER_EXTERNAL_FIND_BY_EMAIL_ENABLED=True
|
||||||
|
SERVER_CHUNKED_PATH=/tmp/chunked_uploads/%Y/%m/%d
|
||||||
|
CHECK_IMPORT_HASH=1
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Workflows / processing (WorkFlowsSettings, env_prefix WORKFLOWS_)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
WORKFLOWS_USE=1
|
||||||
|
WORKFLOWS_HOST=https://api.sarex.io
|
||||||
|
WORKFLOWS_BASE_HOST=https://lk.sarex.io
|
||||||
|
WORKFLOWS_PREFIX=/internal/v1
|
||||||
|
# WORKFLOWS_TIMEOUT=120
|
||||||
|
# WORKFLOWS_REGISTRY=cr.yandex/crp3ccidau046kdj8g9q
|
||||||
|
# WORKFLOWS_TAG=stable
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Внешние API-сервисы (наследуют BaseApiServiceMixin:
|
||||||
|
# host / api_prefix / internal_host / internal_prefix / timeout / enable)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Gateway (env_prefix GATEWAY_)
|
||||||
|
# GATEWAY_HOST=https://api.sarex.io
|
||||||
|
# Gatekeeper (env_prefix GK_)
|
||||||
|
# GK_ENCRYPTION_KEY=
|
||||||
|
# BIM v2 (env_prefix BIMV2_)
|
||||||
|
BIMV2_INTERNAL_HOST=http://bim-backend-v2-service.bim-api
|
||||||
|
BIMV2_TIMEOUT=60
|
||||||
|
# EAV (env_prefix EAV_)
|
||||||
|
EAV_ENABLE=1
|
||||||
|
# Documentation (env_prefix DOCUMENTATION_)
|
||||||
|
# DOCUMENTATION_HOST=https://api.sarex.io
|
||||||
|
# Analytics (env_prefix ANALYTICS_)
|
||||||
|
# ANALYTICS_HOST=https://lk.sarex.io
|
||||||
|
# Users (env_prefix USERS_)
|
||||||
|
# USERS_HOST=https://lk.sarex.io
|
||||||
|
# System log (env_prefix SYSTEM_LOG_)
|
||||||
|
# SYSTEM_LOG_INTERNAL_HOST=
|
||||||
|
# Resources (env_prefix RESOURCES_)
|
||||||
|
# RESOURCES_INTERNAL_HOST=http://localhost:8001
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Measurements (MeasurementSettings, env_prefix MEASUREMENTS_)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
MEASUREMENTS_HOST=https://api.sarex.io/measurements/
|
||||||
|
# MEASUREMENTS_TIMEOUT=180
|
||||||
|
# MEASUREMENTS_WINDOW_SIZE=1000
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ClickHouse (ClickHouseSettings, env_prefix CLICKHOUSE_)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# CLICKHOUSE_HOST=
|
||||||
|
# CLICKHOUSE_PORT=9000
|
||||||
|
# CLICKHOUSE_USER=
|
||||||
|
# CLICKHOUSE_PASSWORD=
|
||||||
|
# CLICKHOUSE_DATABASE=values_db
|
||||||
|
# CLICKHOUSE_TABLE=values
|
||||||
|
# CLICKHOUSE_SECURE=False
|
||||||
|
# CLICKHOUSE_VERIFY=False
|
||||||
|
# CLICKHOUSE_CERT=
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Zitadel (ZitadelSettings, env_prefix ZITADEL_)
|
||||||
|
# ZITADEL_ACCESS_TOKEN приходит из Vault-секрета secrets/data/vault/common/django_auth
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
ZITADEL_HOST=https://zitadel.contour.infra.sarex.tech
|
||||||
|
ZITADEL_ACCESS_TOKEN=
|
||||||
|
# ZITADEL_USERS_ENDPOINT=/v2/users
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Keycloak (KeyCloakSettings env_prefix KC_, KeyCloakSyncSettings env_prefix KC_SYNC)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
KC_SYNC_ENABLE=0
|
||||||
|
KC_USE_REDIRECT_LOGOUT=False
|
||||||
|
# KC_CLIENT_ID=
|
||||||
|
# KC_CLIENT_SECRET=
|
||||||
|
# KC_DISCOVERY_URL=
|
||||||
|
# KC_REALM=sarex
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Трейсинг (TracingConfig, env_prefix TRACING_) + OpenTelemetry
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# TRACING_SERVICE_NAME=backend.sarex-stage
|
||||||
|
# TRACING_ENDPOINT=localhost:4317
|
||||||
|
# TRACING_INSECURE=False
|
||||||
|
# TRACING_ENVIRONMENT=prod
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Comparator / прочее
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
COMPARATOR_URL=https://wb.sarex.io/comparator
|
||||||
|
COMPARATOR_SECTION=sarex-production-storage
|
||||||
|
# COMPARATOR_JWT=
|
||||||
|
# COMPARATOR_BASIC_TOKEN=
|
||||||
|
# WORKFLOWSSETTINGS_HOST=https://api.sarex.io # используется в configmap production.py
|
||||||
|
# WORKFLOWSSETTINGS_REGISTRY=cr.yandex/crp3ccidau046kdj8g9q
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Легаси/интеграции (значения по умолчанию есть в base.py)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# PG_NODE_HOST=127.0.0.1:5000
|
||||||
|
# PG_API_KEY=
|
||||||
|
# PG_MONGO_HOST=localhost
|
||||||
|
# PG_MONGO_PORT=27017
|
||||||
|
# PG_IMPORT_PATH=/home/sarex/pg/import
|
||||||
|
# WIKIMAPIA_API_KEY=
|
||||||
|
# ANALYTICS_IMPORT_METRICS_SHEET_NAME=SRX
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Frontend (sarex-frontend) — сборочные переменные (webpack DefinePlugin)
|
||||||
|
# Базовые хосты сервисов берутся из endpoints.js по ключу ENDPOINT.
|
||||||
|
# =============================================================================
|
||||||
|
# ENDPOINT=prod # stage | prod | preprod | contour | local
|
||||||
|
# SAREX_BACKEND=https://stage.sarex.io/ # прокси-таргет backend при npm start (env.js)
|
||||||
|
# TYPE_OF_HTTP_SERVICE=original
|
||||||
|
# CLIENT_ID=
|
||||||
|
# AMPLITUDE_ENABLED=false
|
||||||
357
apps/django/CONFIGURATION.md
Normal file
357
apps/django/CONFIGURATION.md
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
# Конфигурация проекта 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`
|
||||||
|
рядом с этим файлом.
|
||||||
127
apps/django/ENDPOINTS.md
Normal file
127
apps/django/ENDPOINTS.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# Эндпоинты и хосты сервисов для sarex-frontend
|
||||||
|
|
||||||
|
Документ описывает базовые хосты сервисов, к которым обращается фронтенд-шелл
|
||||||
|
`sarex-frontend`, и удалённые модули (Module Federation), которые он подгружает.
|
||||||
|
Backend для этого приложения — `sarex-backend` (Django), его конфигурация и
|
||||||
|
переменные описаны в `CONFIGURATION.md`, а серверное REST API — в `openapi.yaml`.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
`sarex-frontend` — это host-приложение на **Webpack Module Federation**. Реестр
|
||||||
|
хостов задаётся декларативно в `endpoints.js` (корень репозитория): объект вида
|
||||||
|
`<сервис>.<тип>.<окружение>` → URL. Возможные типы:
|
||||||
|
|
||||||
|
- `api` / `apiV1` / `apiV2` / … — базовый URL REST API сервиса;
|
||||||
|
- `module` — URL `remoteEntry.js` удалённого микрофронтенда;
|
||||||
|
- `gateway` — базовый URL gateway.
|
||||||
|
|
||||||
|
Окружение выбирается сборочной переменной `ENDPOINT` (`process.env.ENDPOINT`,
|
||||||
|
по умолчанию `prod`) в `webpack.common.js`. Значения `endpoints.js[сервис][тип][ENDPOINT]`
|
||||||
|
пробрасываются в код как константы `process.env.<CONST>` через `DefinePlugin`
|
||||||
|
(объект `processEnvByEndpoint`), напр. `endpoints.bim.api[ENDPOINT]` → `BIM_API`.
|
||||||
|
|
||||||
|
Для локального запуска backend-таргет задаётся отдельно в `env.js`
|
||||||
|
(`SAREX_BACKEND`, по умолчанию `https://stage.sarex.io/`) и используется
|
||||||
|
dev-сервером (`webpack.dev.js`) как прокси; собственный Django-backend в окружении
|
||||||
|
`contour` доступен по относительным путям (пустой хост).
|
||||||
|
|
||||||
|
## Окружения (`ENDPOINT`)
|
||||||
|
|
||||||
|
| Значение | Назначение |
|
||||||
|
| --- | --- |
|
||||||
|
| `prod` | Продакшн (`https://api.sarex.io`, `https://modules.sarex.io`) |
|
||||||
|
| `stage` | Stage (`https://stage-api.sarex.io`, `https://stage-modules.sarex.io`) |
|
||||||
|
| `preprod` | Preprod (`https://api.preprod.sarex.io`, `https://modules.preprod.sarex.io`) |
|
||||||
|
| `contour` | Изолированный контур — относительные пути (пустой хост) |
|
||||||
|
| `local` | Локальная разработка (задан не у всех сервисов) |
|
||||||
|
|
||||||
|
> У части сервисов `gateway` определены также специальные ключи `contour_local`
|
||||||
|
> (`https://stage.sarex.io`) и `contour_prod` (`https://lk.sarex.io`).
|
||||||
|
|
||||||
|
## Базовые хосты API-сервисов
|
||||||
|
|
||||||
|
Итоговый URL = `<базовый хост сервиса>` + путь запроса.
|
||||||
|
|
||||||
|
| Сервис (`endpoints.*`) | Константа | `stage` | `prod` | `contour` |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `api.api` | `SAREX_API` | `https://stage-api.sarex.io` | `https://api.sarex.io` | `""` |
|
||||||
|
| `bim.api` | `BIM_API` | `https://stage-api.sarex.io/bim/api/v1` | `https://api.sarex.io/bim/api/v1` | `""` |
|
||||||
|
| `bim.apiV2` | `BIM_API_V2` | `…/bim/api/v2` | `…/bim/api/v2` | `""` |
|
||||||
|
| `bim.files` | `BIM_FILES` | `…/bim` | `…/bim` | `""` |
|
||||||
|
| `workspaces.api` / `workspacesV2.api` | `WORKSPACESV2_API` | `…/workspaces/` | `…/workspaces/` | `/workspaces/` |
|
||||||
|
| `workflows.api` | `WORKFLOWS_API` | `…/workflows` | `…/workflows` | `/workflows` |
|
||||||
|
| `remarks.api` | `REMARKS_API` | `…/remarks/api/v1` | `…/remarks/api/v1` | `/remarks/api/v1` |
|
||||||
|
| `issues.api` | `ISSUES_API` | `…/issues/api/v1` | `…/issues/api/v1` | `/issues/api/v1` |
|
||||||
|
| `issuesBase.api` | `ISSUES_BASE_API` | `…/issues/api` | `…/issues/api` | `/issues/api` |
|
||||||
|
| `flows.api` | — | `…/issues/api/v1` | `…/issues/api/v1` | `/issues/api/v1` |
|
||||||
|
| `inspections.api` | — | `…/inspections/api/v1` | `…/inspections/api/v1` | `/inspections/api` |
|
||||||
|
| `documentations.api` | `DOCUMENTATIONS_API` | `…/documentations/api/v1` | `…/documentations/api/v1` | `/documentations/api/v1` |
|
||||||
|
| `pm.api` | — | `…/documentations/api/v1` | `…/documentations/api/v1` | `/documentations/api/v1` |
|
||||||
|
| `analyticsV2.api` | — | `…/analytics-v2/api/v1` | `…/analytics-v2/api/v1` | `/analytics-v2/api/v1` |
|
||||||
|
| `analytics.api` | `ANALYTICS_API` | `…/analytics` | `…/analytics` | `/analytics` |
|
||||||
|
| `processes.api` | `PROCESSES_API` | `…/flows/api/v1` | `…/flows/api/v1` | `/flows/api/v1` |
|
||||||
|
| `gateway.gateway` | `GATEWAY` | `…/gateway` | `…/gateway` | (local: `http://localhost:9000/gateway`) |
|
||||||
|
| `gateway.api` | `GATEWAY_API` | `…/gateway/api/v1` | `…/gateway/api/v1` | `/gateway/api/v1` |
|
||||||
|
| `gateway.apiV2` | `GATEWAY_API_V2` | `…/gateway/api/v2` | `…/gateway/api/v2` | `/gateway/api/v2` |
|
||||||
|
| `eav.api` | `EAV_API` | `…/eav/api/v0` | `…/eav/api/v0` | `/eav/api/v0` |
|
||||||
|
| `eav.apiV1…apiV4` | `EAV_API_V1…V4` | `…/eav/api/v1…v4` | `…/eav/api/v1…v4` | `/eav/api/v1…v4` |
|
||||||
|
| `notifications.api` | `NOTIFICATIONS_API` | `…/lambdas/notification/` | `…/lambdas/notification/` | `""` |
|
||||||
|
| `lambdas.api` | `LAMBDAS_API` | `…/lambdas` | `…/lambdas` | `/lambdas` |
|
||||||
|
| `automations.api` | `AUTOMATIONS_API` | `…/automation/api/v1` | `…/automation/api/v1` | `/automation/api/v1` |
|
||||||
|
| `orchestrator.api` | `ORCHESTRATOR_API` | `…/orchestrator` | `…/orchestrator/api` | `/orchestrator/api` |
|
||||||
|
|
||||||
|
> `…` = `https://stage-api.sarex.io` (stage) или `https://api.sarex.io` (prod).
|
||||||
|
> Собственный Django-backend (`sarex-backend`) в контуре обслуживается по
|
||||||
|
> относительным путям `/api/...` — см. `openapi.yaml`.
|
||||||
|
|
||||||
|
## Удалённые модули (Module Federation, `remoteEntry.js`)
|
||||||
|
|
||||||
|
Хост подгружает микрофронтенды по URL из `endpoints.<сервис>.module[ENDPOINT]`.
|
||||||
|
Базовый хост модулей: `https://stage-modules.sarex.io` (stage) /
|
||||||
|
`https://modules.sarex.io` (prod); в контуре — относительные пути.
|
||||||
|
|
||||||
|
| Сервис | Константа | Путь `module` (относительный, контур) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `workspaces` | `WORKSPACES_MODULE` | `/workspaces/module/remoteEntry.js` |
|
||||||
|
| `workspacesV2` | `WORKSPACESV2_MODULE` | `/workspaces-v2/module/remoteEntry.js` |
|
||||||
|
| `workflows` | `WORKFLOWS_MODULE` | `/workflows/module/remoteEntry.js` |
|
||||||
|
| `remarks` | `REMARKS_MODULE` | `/remarks/static/module/remoteEntry.js` |
|
||||||
|
| `issues` | `ISSUES_MODULE` | `/issues/static/module/remoteEntry.js` |
|
||||||
|
| `flows` | `FLOWS_MODULE` | `/flows/static/module/remoteEntry.js` |
|
||||||
|
| `inspections` | `INSPECTIONS_MODULE` | `/inspections/static/module/remoteEntry.js` |
|
||||||
|
| `documentations` | `DOCUMENTATIONS_MODULE` | `/documentations/static/module/remoteEntry.js` |
|
||||||
|
| `pm` | `PM_MODULE` | `/pm/module/remoteEntry.js` |
|
||||||
|
| `projects` | `PROJECTS_MODULE` | `/projects/static/module/remoteEntry.js` |
|
||||||
|
| `analyticsV2` | `ANALYTICS_MODULE` | `/analytics-v2/static/module/remoteEntry.js` |
|
||||||
|
| `reviews` | `REVIEWS_MODULE` | `/reviews/static/module/remoteEntry.js` |
|
||||||
|
| `administration` | `ADMINISTRATION_MODULE` | `/control-interface/modules/admin/remoteEntry.js` |
|
||||||
|
| `adminProc` | `ADMIN_PROC_MODULE` | `/admin-frontend/static/module/remoteEntry.js` |
|
||||||
|
| `assets` | `ASSETS_MODULE` | `/control-interface/modules/assets/remoteEntry.js` |
|
||||||
|
| `premises` | `PREMISES_MODULE` | `/premises/static/module/remoteEntry.js` |
|
||||||
|
| `contracts` | `CONTRACTS_MODULE` | `/cotracts/static/module/remoteEntry.js` |
|
||||||
|
| `transmittal` | `TRANSMITTAL_MODULE` | `/transmittal/static/module/remoteEntry.js` |
|
||||||
|
| `prescriptions` | `PRESCRIPTIONS_MODULE` | `/prescriptions/static/module/remoteEntry.js` |
|
||||||
|
| `rfi` | `RFI_MODULE` | `/rfi/static/module/remoteEntry.js` |
|
||||||
|
| `assistant` | — | `/assistant/static/module/remoteEntry.js` |
|
||||||
|
|
||||||
|
Список подключаемых в ЛК модулей (пункты меню) дублируется на стороне backend в
|
||||||
|
`SAREX_MODULES` (ConfigMap `django-configmap`): `remarks`, `issues`,
|
||||||
|
`documentations`, `reviews`, `processes`, `rfi`, `transmittal`.
|
||||||
|
|
||||||
|
## Backend, обслуживающий шелл
|
||||||
|
|
||||||
|
Основной API самого шелла (аутентификация, пользователи, настройки приложения,
|
||||||
|
проекты/цели/миссии, аналитика) — это `sarex-backend` под префиксом `/api/...`
|
||||||
|
(и `/internal/...` для внутрикластерных вызовов). Полное описание серверных
|
||||||
|
эндпоинтов приведено в `openapi.yaml`. Ключевые группы:
|
||||||
|
|
||||||
|
| Префикс | Назначение |
|
||||||
|
| --- | --- |
|
||||||
|
| `/api/token…`, `/api/auth/…`, `/api/login`, `/api/logout` | Аутентификация и JWT |
|
||||||
|
| `/api/app-settings/` | Настройки приложения |
|
||||||
|
| `/api/core/…` | Пользователи, компании, цели, миссии, ортофото, облака точек и т.д. |
|
||||||
|
| `/api/client/…` | Клиентский дашборд, загрузки, self-сервис |
|
||||||
|
| `/api/analytics/…` | Аналитические дашборды, метрики, виджеты |
|
||||||
|
| `/api/map/…` | Кадастр и заметки на карте |
|
||||||
|
| `/api/pg/…` | Облака точек, экспорт, измерения |
|
||||||
|
| `/internal/client/…` | Внутренние вызовы (настройки, токены) |
|
||||||
417
apps/django/FRONTEND_REQUESTS.md
Normal file
417
apps/django/FRONTEND_REQUESTS.md
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
# Полный перечень запросов sarex-frontend
|
||||||
|
|
||||||
|
Извлечено из исходников `src/` и `modules/`: обёртки `httpService.{get,post,put,patch,delete}Request`, вызовы `fetch`, эндпоинты RTK Query (`builder.query/mutation`). Базовый хост подставляется по ключу `service` (для `httpService`, см. `src/Model/api/hosts.ts`) либо по `env.*` из `endpoints.js`; выбор окружения — переменной сборки `ENDPOINT`. В путях `{param}` — подстановки, `?a=&b=` — query-параметры, заданные в коде. Хосты приведены для `prod`.
|
||||||
|
|
||||||
|
**Всего вызовов: 392** — httpService: 296, fetch: 76, RTK Query: 20.
|
||||||
|
|
||||||
|
|
||||||
|
## `sarex` — Django backend (`sarex-backend`), host `/` (в контуре относительные пути)
|
||||||
|
|
||||||
|
Уникальных запросов: 206
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `(динамический URL — передаётся переменной)` | src/Model/BaseApi.ts (+2) |
|
||||||
|
| POST | `(динамический URL — передаётся переменной)` | src/Model/BaseApi.ts (+1) |
|
||||||
|
| PUT | `(динамический URL — передаётся переменной)` | src/Model/BaseApi.ts (+3) |
|
||||||
|
| PATCH | `(динамический URL — передаётся переменной)` | src/Model/BaseApi.ts |
|
||||||
|
| DELETE | `(динамический URL — передаётся переменной)` | src/Model/BaseApi.ts |
|
||||||
|
| POST | `/api/analytics/attachments/` | src/Components/ImageDropZone/ImageDropZone.jsx |
|
||||||
|
| GET | `/api/analytics/attachments/?dashboard={dashboardId}` | src/Components/ImageDropZone/hook.js |
|
||||||
|
| DELETE | `/api/analytics/attachments/{id}/` | src/Components/ImageDropZone/ImageDropZone.jsx |
|
||||||
|
| DELETE | `/api/analytics/attributes-options/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/attributes/{id}/` | src/Model/api.js |
|
||||||
|
| PUT | `/api/analytics/dashboards/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/dashboards/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/analytics/expressions/` | modules/analytics/entities/ComputationValue/ComputationValueEntity.ts |
|
||||||
|
| POST | `/api/analytics/expressions/` | modules/analytics/entities/ComputationValue/ComputationValueEntity.ts |
|
||||||
|
| DELETE | `/api/analytics/expressions/{id}/` | modules/analytics/entities/ComputationValue/ComputationValueEntity.ts |
|
||||||
|
| PUT | `/api/analytics/expressions/{newComputationValue.id}` | modules/analytics/entities/ComputationValue/ComputationValueEntity.ts |
|
||||||
|
| PATCH | `/api/analytics/folders/{body.id}/` | modules/analytics/store/api/api.ts |
|
||||||
|
| DELETE | `/api/analytics/folders/{id}/` | modules/analytics/store/api/api.ts (+1) |
|
||||||
|
| PATCH | `/api/analytics/groups/{body.id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/groups/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/metric2widget/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/analytics/metrics/` | modules/analytics/entities/Metric/MetricEntity.ts |
|
||||||
|
| DELETE | `/api/analytics/metrics/{id}/` | modules/analytics/store/api/api.ts |
|
||||||
|
| PUT | `/api/analytics/metrics/{metric.id}/` | modules/analytics/store/api/api.ts |
|
||||||
|
| GET | `/api/analytics/metrics/{params \|\|` | modules/analytics/store/api/api.ts |
|
||||||
|
| GET | `/api/analytics/texts/?dashboard={id}` | modules/widget/store/widgets.ts |
|
||||||
|
| PATCH | `/api/analytics/texts/{body.id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/texts/{id}/` | src/Model/api.js |
|
||||||
|
| POST | `/api/analytics/values/import_xlsx/` | modules/analytics/store/api/api.ts |
|
||||||
|
| PATCH | `/api/analytics/values/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/values/{valueId}/` | src/Model/api.js |
|
||||||
|
| PATCH | `/api/analytics/widgets/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/analytics/widgets/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/client/dashboard/feed/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/client/dashboard/targets/{orderId}/feed/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/client/dashboard/targets/{orderId}/feed/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/client/dashboard/targets/{targetId}/request_survey/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/client/dashboard/webcams/` | src/Model/api.js |
|
||||||
|
| GET | `/api/client/folders/?target={targetId}&limit=10000` | src/Model/api.js |
|
||||||
|
| PATCH | `/api/client/folders/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/client/folders/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/client/settings/` | src/Model/actions.js |
|
||||||
|
| PUT | `/api/client/settings/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/client/uploads/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/client/uploads/?target={targetId}&limit=10000` | src/Model/actions.js |
|
||||||
|
| GET | `/api/client/uploads/{doc}/layers/` | src/Model/actions.js |
|
||||||
|
| DELETE | `/api/client/uploads/{fileId}/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/client/uploads/{fileId}/translate_to_svf/` | src/Model/actions.js |
|
||||||
|
| PATCH | `/api/client/uploads/{id}/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/commons/cs/` | src/Model/actions.js (+1) |
|
||||||
|
| GET | `/api/core/admin/companies/` | src/shared/api/fetch/company.api.ts |
|
||||||
|
| POST | `/api/core/c2s-comparisons/` | src/Components/Viewer/Panels/ComparisonsPanel/store/api/api.ts |
|
||||||
|
| GET | `/api/core/companies/` | modules/targetsTree/repositories/CompanyRepository/RESTCompanyRepository.ts (+1) |
|
||||||
|
| POST | `/api/core/contour/export/` | src/Model/api.js |
|
||||||
|
| GET | `/api/core/contour/export/?pointcloud={pointcloudId}` | src/Model/api.js |
|
||||||
|
| GET | `/api/core/materials/` | modules/measurements/components/volume/store/materials.ts |
|
||||||
|
| POST | `/api/core/media-folders/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| DELETE | `/api/core/media-folders/{id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| PATCH | `/api/core/media-folders/{params.id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| POST | `/api/core/mesh/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/core/mesh/{id}/` | src/Model/api.js |
|
||||||
|
| POST | `/api/core/missions/import/create/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/core/missions/{id}/` | src/Model/api.js |
|
||||||
|
| POST | `/api/core/mrpa/` | src/shared/api/fetch/mrpa.api.ts |
|
||||||
|
| POST | `/api/core/mrpa/list/` | src/shared/api/fetch/mrpa.api.ts |
|
||||||
|
| DELETE | `/api/core/mrpa/{id}/` | src/shared/api/fetch/mrpa.api.ts |
|
||||||
|
| GET | `/api/core/orthophotos/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/core/orthophotos/{orthoId}/altitude_and_temperature/?points={lng},{lat}` | src/Model/api.js |
|
||||||
|
| GET | `/api/core/panoramas/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/core/panoramas/{id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| PATCH | `/api/core/panoramas/{params.id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| DELETE | `/api/core/photos/{id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| PATCH | `/api/core/photos/{params.id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| GET | `/api/core/pointclouds/?mission={mission}&comparison_type={type}` | src/Components/Viewer/Panels/ComparisonsPanel/store/api/api.ts |
|
||||||
|
| POST | `/api/core/polygons/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/core/polygons/?with_links_folder{targetIdQuery}` | src/Model/api.js |
|
||||||
|
| POST | `/api/core/state/` | src/Components/Viewer/Services/States/Api/ViewerStatesApi.ts |
|
||||||
|
| GET | `/api/core/state/?pointcloud_id={pointCloudId}` | src/Components/Viewer/Services/States/Api/ViewerStatesApi.ts |
|
||||||
|
| PATCH | `/api/core/state/{id}/` | src/Components/Viewer/Services/States/Api/ViewerStatesApi.ts |
|
||||||
|
| DELETE | `/api/core/state/{id}/` | src/Components/Viewer/Services/States/Api/ViewerStatesApi.ts |
|
||||||
|
| GET | `/api/core/targets/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/core/users/` | modules/dashboards/models/entities/Users/UsersEntity.ts |
|
||||||
|
| DELETE | `/api/core/videos/{id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| PATCH | `/api/core/videos/{params.id}/` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| GET | `/api/map/cadastre/point/?point={point}` | src/Model/actions.js |
|
||||||
|
| GET | `/api/map/notes/folders/?orthophoto={targetId}` | src/Model/api.js |
|
||||||
|
| PATCH | `/api/map/notes/folders/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/map/notes/folders/{id}/` | src/Model/api.js |
|
||||||
|
| PATCH | `/api/mar/tasks/{taskId}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/mar/tasks_attachments/{id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/mar/tasks_groups_attachments/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/notifications` | modules/targetsTree/repositories/TargetRepository/RESTTragetRepository.ts (+1) |
|
||||||
|
| POST | `/api/pg/attachments/` | src/Model/actions.js (+1) |
|
||||||
|
| DELETE | `/api/pg/attachments/{id}/` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/compare/c2c/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/compare/c2c/?pointcloud={pointcloud}` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/compare/c2s/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/compare/t2t/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/compare/t2t/?pointcloud={pointcloud}` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/export/pointcloud/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/export/pointcloud/?pointcloud={pointcloudId}` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/measurements/` | src/Model/Store/voxelVolumeStore.js (+1) |
|
||||||
|
| GET | `/api/pg/measurements/?target={targetId}` | src/Model/actions.js |
|
||||||
|
| PUT | `/api/pg/measurements/{idOnServer}/` | src/Model/Store/voxelVolumeStore.js |
|
||||||
|
| DELETE | `/api/pg/measurements/{idOnServer}/` | src/Model/Store/voxelVolumeStore.js |
|
||||||
|
| PUT | `/api/pg/measurements/{measurement.idOnServer}/` | src/Model/actions.js |
|
||||||
|
| DELETE | `/api/pg/measurements/{measurementId}/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/orthomosaicexport/?mission_id={missionId}` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/pdf-overlays/` | src/Model/api.js |
|
||||||
|
| DELETE | `/api/pg/pdf-overlays/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/pointclouds/{id}/comparisons/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/pointclouds/{pointCloudId}/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/pointclouds/{pointCloudId}/altitude/?points={formattedPoints}` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/pointclouds/{pointCloudId}/baseplane_volume/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/pointclouds/{pointCloudId}/measurements/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/pointclouds/{pointCloudId}/measurements/delete_measurements/` | src/Model/actions.js |
|
||||||
|
| DELETE | `/api/pg/pointclouds/{pointCloudId}/measurements/{idOnServer}/` | src/Model/actions.js |
|
||||||
|
| PATCH | `/api/pg/pointclouds/{pointCloudId}/measurements/{measurementId}/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/pointclouds/{pointCloudId}/project_volume/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/pointclouds/{pointCloudId}/volume/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/pointclouds/{pointcloud}/comparisons/` | src/Model/api.js |
|
||||||
|
| POST | `/api/pg/pointclouds/{targetId}/measurements/{measurementId}/calculate/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pg/projects/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/{id}/images/` | src/Model/actions.js |
|
||||||
|
| PATCH | `/api/pg/projects/{id}/images/{photo.id}/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/projects/{id}/status/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/{projectId}/build/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/projects/{projectId}/images/{GCPName ? `?marker={GCPName}` :` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/{projectId}/init/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pg/projects/{projectId}/markers/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/{projectId}/optimize_cameras/` | src/Model/actions.js |
|
||||||
|
| POST | `/api/pg/projects/{projectId}/update_marker/` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pm/dms/?target={targetId}` | src/Model/actions.js |
|
||||||
|
| GET | `/api/pm/msp/projects/?bundle_id={bundleID}&schema=tiny` | modules/pm/gate/repositories/SelectedKSGProjectRepository/WorkspaceSelectedKSGProjectRepository.ts |
|
||||||
|
| DELETE | `/api/pm/msp/projects/{id}/` | src/Model/api.js |
|
||||||
|
| GET | `/api/pm/msp/projects/{id}/profiles/` | modules/pm/gate/repositories/TaskResourceConnectionBaseRepository/TaskResourceConnectionBaseRepository.ts |
|
||||||
|
| GET | `/api/pm/msp/resources-tasks/?projects={project}` | modules/pm/gate/repositories/TaskResourcesConnectionsRepository/TaskResourcesConnectionsRepository.ts |
|
||||||
|
| GET | `/api/pm/msp/tasks/?project={project}{query ? `&{query}` :` | modules/pm/gate/repositories/TasksRepository/TasksRepository.ts |
|
||||||
|
| GET | `/api/pm/msp/tasks/{id}/` | modules/pm/gate/repositories/TasksRepository/TasksRepository.ts |
|
||||||
|
| GET | `/api/workflows/?target={id}` | modules/targetsV2_OLD_DEPRECATED/services/TargetTreeService/TargetTreeService.ts |
|
||||||
|
| GET | `/api/workflows/?target={this.targetId}` | modules/missions/controllers/MissionsCardList/MissionsCardListController.ts |
|
||||||
|
| POST | `Analytic.baseUrl` | src/Model/api.js |
|
||||||
|
| POST | `ClientFolders.baseUrl` | src/Model/api.js |
|
||||||
|
| GET | `DashboardsAPI.baseUrl` | src/Model/api.js |
|
||||||
|
| POST | `DashboardsAPI.baseUrl` | src/Model/api.js |
|
||||||
|
| POST | `GroupsApi.baseUrl` | src/Model/api.js |
|
||||||
|
| POST | `MetricsApi.baseUrl` | modules/analytics/store/api/api.ts |
|
||||||
|
| GET | `MetricsFoldersAPI.baseUrl` | modules/analytics/store/api/api.ts |
|
||||||
|
| POST | `MetricsFoldersAPI.baseUrl` | modules/analytics/store/api/api.ts |
|
||||||
|
| POST | `NotesFolders.baseUrl` | src/Model/api.js |
|
||||||
|
| POST | `ProjectsAPI.taskUrl` | src/Model/api.js |
|
||||||
|
| PUT | `StreamFile.baseUrl` | src/Model/api.js |
|
||||||
|
| GET | `host` | modules/control/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| POST | `host` | modules/control/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| GET | `hostGetInfo` | modules/control/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| GET | `hostInfo` | modules/account/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| POST | `hostInfo` | modules/account/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| POST | `hostUpdatePassword` | modules/account/entities/Storage/StorageRESTEntity.ts |
|
||||||
|
| GET | `hosts` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| POST | `hosts` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| POST | `hosts.expression2widget` | modules/widget/entities/Expressions/ExpressionEntity.ts |
|
||||||
|
| GET | `hosts.expressions` | modules/widget/entities/Expressions/ExpressionEntity.ts |
|
||||||
|
| GET | `link` | src/Model/actions.js |
|
||||||
|
| POST | `mediaEndpoint` | src/Model/Media/api.ts |
|
||||||
|
| GET | `nextUrl` | src/Model/MediaSidebar/Api/MediaModelApi.ts |
|
||||||
|
| POST | `panoramasEndpoint` | src/Model/Media/api.ts |
|
||||||
|
| POST | `photosEndpoint` | src/Model/Media/api.ts |
|
||||||
|
| GET | `this.path` | modules/analytics/entities/Folder/FolderEntity.ts |
|
||||||
|
| GET | `this.pathname` | modules/missions/entity/Mission/MissionRESTEntity.ts |
|
||||||
|
| GET | `this.props.url` | modules/auth/TokenIssuer.ts |
|
||||||
|
| POST | `this.uploadUrl` | src/Model/streamFile.ts |
|
||||||
|
| GET | `url` | src/Model/api.js |
|
||||||
|
| GET | `url.toString()` | modules/targetsTree/repositories/TargetRepository/RESTTragetRepository.ts (+2) |
|
||||||
|
| POST | `videosEndpoint` | src/Model/Media/api.ts |
|
||||||
|
| POST | `{Export.baseUrlHorizontal}` | src/Model/api.js |
|
||||||
|
| POST | `{Export.urlTablePoints}` | src/Model/api.js |
|
||||||
|
| POST | `{Export.urlTablePoints}pointcloud/` | src/Model/api.js |
|
||||||
|
| PATCH | `{ProjectsAPI.taskUrl}{body.id}/` | src/Model/api.js |
|
||||||
|
| DELETE | `{ProjectsAPI.taskUrl}{id}/` | src/Model/api.js |
|
||||||
|
| GET | `{TrackingMetaData.trackerUrl}events/` | src/Model/api.js |
|
||||||
|
| GET | `{hosts.expression2widget}?widget={id}` | modules/widget/entities/Expressions/ExpressionEntity.ts |
|
||||||
|
| PUT | `{hosts.expression2widget}{body.id}/` | modules/widget/entities/Expressions/ExpressionEntity.ts |
|
||||||
|
| DELETE | `{hosts.expression2widget}{id}/` | modules/widget/entities/Expressions/ExpressionEntity.ts |
|
||||||
|
| GET | `{hosts}aggregates/` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| POST | `{hosts}{body.dashboboardId}/copy/` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| PUT | `{hosts}{body.id}/` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| DELETE | `{hosts}{id}/` | modules/dashboards/models/entities/Dashboards/DashboardsEntity.ts |
|
||||||
|
| GET | `{host}api/tracking/meta/contractor/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}api/tracking/meta/vehicle/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}api/tracking/meta/vehicletracker/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}api/tracking/meta/zones/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| POST | `{host}api/tracking/meta/zones/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| PUT | `{host}api/tracking/meta/zones/{id}` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}api/tracking/stats-zoned/?{queryParams.toString()}` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}buffer/` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{host}entries-log/?{queryParams.toString()}` | modules/vehicle-tracking/methods/inner/api.ts |
|
||||||
|
| GET | `{panoramasEndpoint}?mission={mission}` | src/Model/Media/api.ts |
|
||||||
|
| PATCH | `{panoramasEndpoint}{panoramaId}` | src/Model/Media/api.ts |
|
||||||
|
| GET | `{photosEndpoint}?mission={mission}` | src/Model/Media/api.ts |
|
||||||
|
| PATCH | `{photosEndpoint}{photoId}/` | src/Model/Media/api.ts |
|
||||||
|
| GET | `{this.pathname}?{searchParams.toString()}` | modules/targets/entities/Target/TargetRESTEntity.ts |
|
||||||
|
| PATCH | `{this.pathname}{params.id}/` | modules/missions/entity/Mission/MissionRESTEntity.ts |
|
||||||
|
| DELETE | `{this.pathname}{params.id}/` | modules/missions/entity/Mission/MissionRESTEntity.ts |
|
||||||
|
| GET | `{videosEndpoint}?mission={mission}` | src/Model/Media/api.ts |
|
||||||
|
| PATCH | `{videosEndpoint}{videoId}` | src/Model/Media/api.ts |
|
||||||
|
|
||||||
|
## `sarexApi` — `https://api.sarex.io` (stage `https://stage-api.sarex.io`)
|
||||||
|
|
||||||
|
Уникальных запросов: 23
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `(динамический URL — передаётся переменной)` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+3) |
|
||||||
|
| GET | `/flows/api/v1/{entity}/tasks-count/` | src/Model/actions.js |
|
||||||
|
| GET | `/transmittals/api/v1/transmittals/count` | src/Model/actions.js |
|
||||||
|
| GET | `pulse/api/core/check_admin/` | src/Model/Pulse/api.ts |
|
||||||
|
| GET | `pulse/api/core/user/` | src/Model/Pulse/api.ts |
|
||||||
|
| GET | `{baseUrl}/` | modules/sarexPulse/SarexPulse/banners/api/bannersPublicApi.ts (+1) |
|
||||||
|
| POST | `{baseUrl}/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+2) |
|
||||||
|
| DELETE | `{baseUrl}/bulk_delete/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+1) |
|
||||||
|
| GET | `{baseUrl}/check_new/` | modules/sarexPulse/SarexPulse/changelog/api/changelogPublicApi.ts |
|
||||||
|
| GET | `{baseUrl}/last-viewed` | modules/sarexPulse/SarexPulse/changelog/api/notificationApi.ts |
|
||||||
|
| POST | `{baseUrl}/mark-all-viewed` | modules/sarexPulse/SarexPulse/changelog/api/notificationApi.ts |
|
||||||
|
| POST | `{baseUrl}/mark-viewed` | modules/sarexPulse/SarexPulse/changelog/api/notificationApi.ts |
|
||||||
|
| GET | `{baseUrl}/unread-count` | modules/sarexPulse/SarexPulse/changelog/api/notificationApi.ts |
|
||||||
|
| POST | `{baseUrl}/upload` | modules/sarexPulse/SarexPulse/shared/api/mediaApi.ts |
|
||||||
|
| POST | `{baseUrl}/viewed/` | modules/sarexPulse/SarexPulse/banners/api/bannersPublicApi.ts (+1) |
|
||||||
|
| GET | `{baseUrl}/{id}/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+1) |
|
||||||
|
| PUT | `{baseUrl}/{id}/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+1) |
|
||||||
|
| DELETE | `{baseUrl}/{id}/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+2) |
|
||||||
|
| POST | `{baseUrl}/{id}/close/` | modules/sarexPulse/SarexPulse/banners/api/bannersPublicApi.ts |
|
||||||
|
| POST | `{baseUrl}/{id}/dislike/` | modules/sarexPulse/SarexPulse/changelog/api/changelogPublicApi.ts |
|
||||||
|
| POST | `{baseUrl}/{id}/like/` | modules/sarexPulse/SarexPulse/changelog/api/changelogPublicApi.ts |
|
||||||
|
| POST | `{baseUrl}/{id}/publish/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+1) |
|
||||||
|
| POST | `{baseUrl}/{id}/unpublish/` | modules/sarexPulse/SarexPulse/banners/api/bannersAdminApi.ts (+1) |
|
||||||
|
|
||||||
|
## `bim` — `https://api.sarex.io/bim`
|
||||||
|
|
||||||
|
Уникальных запросов: 17
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PATCH | `/api/v1/bims/{bimId}` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/bims/{bimId}/archive` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/bims/{bimId}/changes` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/bims/{bimId}/changes` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/bims/{bimId}/deviations` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/bims/{bimId}/elements` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/bims/{bimId}/sarexid/{sarexIds}` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/bims/{bimId}/unarchive` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/bims/{srcBIMId}/merge/{dstBIMId}` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/changes` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/comparisons` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/elements/{elementId}/properties` | src/Model/BIM/api.js |
|
||||||
|
| PUT | `/api/v1/elements/{elementId}/properties` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/elements/{elementId}/stats` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v1/targets/{targetId}/bims` | src/Model/BIM/api.js |
|
||||||
|
| GET | `/api/v1/targets/{targetId}/bims?archived=1` | src/Model/BIM/api.js |
|
||||||
|
| POST | `/api/v2/deviations` | src/Model/BIM/api.js |
|
||||||
|
|
||||||
|
## `workflows` — `https://api.sarex.io/workflows`
|
||||||
|
|
||||||
|
Уникальных запросов: 2
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/workflows/{comparison.c2c_workflow},{comparison.t2t_workflow}/state` | src/Components/Viewer/Panels/ComparisonsPanel/components/MissionComparison/MissionComparison.tsx |
|
||||||
|
| GET | `/api/v1/workflows/{workflowId}` | src/Components/Viewer/Panels/FilesPanel/hooks/useGetWorkflowStatus.ts |
|
||||||
|
|
||||||
|
## `workspaces` — `https://api.sarex.io/workspaces`
|
||||||
|
|
||||||
|
Уникальных запросов: 1
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/workspaces/{workspaceID}` | modules/dashboards/view-model/WorkspacePreviewSelectorViewModel/WorkspacePreviewSelectorViewModel.ts |
|
||||||
|
|
||||||
|
## `gateway` — `https://api.sarex.io/gateway`
|
||||||
|
|
||||||
|
Уникальных запросов: 1
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/resources` | src/Model/actions.js |
|
||||||
|
|
||||||
|
## `comparisons` — `https://api.sarex.io/comparisons`
|
||||||
|
|
||||||
|
Уникальных запросов: 1
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/{id}/` | src/Components/Viewer/Panels/LayersPanel/store/comparisonsLayers.ts |
|
||||||
|
|
||||||
|
## `notifications` — `https://api.sarex.io/lambdas/notification`
|
||||||
|
|
||||||
|
Уникальных запросов: 1
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `(динамический URL — передаётся переменной)` | modules/notifications/email-notification/email-notifier.ts |
|
||||||
|
|
||||||
|
## `processes` (fetch) — `https://api.sarex.io/flows/api/v1`
|
||||||
|
|
||||||
|
Уникальных запросов: 32
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/documents/` | modules/reviews/api/index.ts |
|
||||||
|
| GET | `/documents/?full=true{document_ids ? `&document_ids={document_ids}` :` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/documents/set-status/?document_ids={ids.join(",")}` | modules/reviews/api/index.ts |
|
||||||
|
| PUT | `/documents/{id}/` | modules/reviews/api/index.ts |
|
||||||
|
| POST | `/flows/` | modules/processes/store/api/index.ts |
|
||||||
|
| GET | `/flows/count_flows_by_resource/?{params}` | modules/processes/store/api/index.ts |
|
||||||
|
| PUT | `/flows/{body.id}/?full=true` | modules/processes/store/api/index.ts |
|
||||||
|
| POST | `/flows/{id}/copy/?full=true` | modules/processes/store/api/index.ts |
|
||||||
|
| POST | `/reviewers/` | modules/processes/store/api/index.ts |
|
||||||
|
| PUT | `/reviewers/{body.id}/` | modules/processes/store/api/index.ts |
|
||||||
|
| DELETE | `/reviewers/{id}/` | modules/processes/store/api/index.ts |
|
||||||
|
| POST | `/reviews/` | modules/reviews/api/index.ts |
|
||||||
|
| GET | `/reviews/count_by_resource_id/?{params}` | modules/reviews/api/index.ts |
|
||||||
|
| GET | `/reviews/count_by_reviewer_id/?current_reviewers={query}` | modules/reviews/api/index.ts |
|
||||||
|
| PUT | `/reviews/{body.id}/` | modules/reviews/api/index.ts |
|
||||||
|
| DELETE | `/reviews/{id}/` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/reviews/{id}/approve/` | modules/reviews/api/index.ts |
|
||||||
|
| GET | `/reviews/{id}/documents/` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/reviews/{id}/update-bundles/` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/reviews/{reviewId}/change_reviewers/` | modules/reviews/api/index.ts |
|
||||||
|
| PUT | `/reviews/{reviewId}/documents/` | modules/reviews/api/index.ts |
|
||||||
|
| POST | `/statuses/` | modules/processes/store/api/index.ts |
|
||||||
|
| PUT | `/statuses/{body.id}/` | modules/processes/store/api/index.ts |
|
||||||
|
| DELETE | `/statuses/{id}/` | modules/processes/store/api/index.ts |
|
||||||
|
| POST | `/steps/` | modules/processes/store/api/index.ts |
|
||||||
|
| PUT | `/steps/{body.id}/?full=true` | modules/processes/store/api/index.ts |
|
||||||
|
| GET | `/steps/{stepId}/active_reviews/` | modules/processes/store/api/index.ts |
|
||||||
|
| GET | `/steps/{stepId}/get_reviewers/?review_id={reviewId}` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/steps/{stepId}/update_reviewers/` | modules/processes/store/api/index.ts |
|
||||||
|
| GET | `/tasks/reviewers-max-end-dates/?{query}` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/tasks/{id}/change-duration/` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `/tasks/{id}/change-priority/` | modules/reviews/api/index.ts |
|
||||||
|
|
||||||
|
## `orchestrator` (fetch) — `https://api.sarex.io/orchestrator/api`
|
||||||
|
|
||||||
|
Уникальных запросов: 3
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/process` | modules/reviews/api/marks.ts |
|
||||||
|
| GET | `/process/{id}` | modules/reviews/api/marks.ts |
|
||||||
|
| POST | `/sign` | modules/reviews/api/marks.ts |
|
||||||
|
|
||||||
|
## `automation` (fetch) — `https://api.sarex.io/automation/api/v1`
|
||||||
|
|
||||||
|
Уникальных запросов: 1
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/automations` | modules/automation/store/automation.ts |
|
||||||
|
|
||||||
|
## `analytics-v2` (RTK Query) — `https://api.sarex.io/analytics-v2/api/v1`
|
||||||
|
|
||||||
|
Уникальных запросов: 19
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/commons/wiki.get` | src/Components/Wiki/apiWiki/api.ts |
|
||||||
|
| POST | `/api/commons/wiki.media.save` | src/Components/Wiki/apiWiki/api.ts |
|
||||||
|
| POST | `/api/commons/wiki.update` | src/Components/Wiki/apiWiki/api.ts |
|
||||||
|
| GET | `/api/core/media-notes-attachments/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| POST | `/api/core/media-notes-attachments/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| DELETE | `/api/core/media-notes-attachments/{attachmentId}` | src/Model/MediaNotes/api.ts |
|
||||||
|
| GET | `/api/core/media-notes-comments/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| POST | `/api/core/media-notes-comments/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| DELETE | `/api/core/media-notes-comments/{body.commentId}/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| GET | `/api/core/media-notes/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| POST | `/api/core/media-notes/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| PATCH | `/api/core/media-notes/{body.id}/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| DELETE | `/api/core/media-notes/{params.noteId}/` | src/Model/MediaNotes/api.ts |
|
||||||
|
| POST | `/api/core/targets/v2/{target_id}/calculate_panoramas_relative_orientation/` | src/Components/MediaViewer/MediaPanorama/panoramaApi.ts |
|
||||||
|
| GET | `/api/core/targets/{target}/missions/` | src/Components/Viewer/Panels/Media/MediaApi/missionsApi.ts |
|
||||||
|
| GET | `/api/notifications/?page={page}` | src/Model/AeroNoty/api.ts |
|
||||||
|
| POST | `/api/notifications/update/` | src/Model/AeroNoty/api.ts |
|
||||||
|
| GET | `/workflows/get_items_by_generic_key/?model=target&object_id={target_id}` | src/Components/MediaViewer/MediaPanorama/panoramaApi.ts |
|
||||||
|
| GET | `wfs` | src/Model/GIS/api.ts |
|
||||||
|
|
||||||
|
## Прочее (static-конфиг, внешние сервисы, wrappers с динамическим URL)
|
||||||
|
|
||||||
|
Уникальных запросов: 13
|
||||||
|
|
||||||
|
| Метод | Путь | Файл (источник) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `(динамический URL — передаётся переменной)` | src/Components/Viewer/Panels/Media/MediaRedux.tsx |
|
||||||
|
| GET | `/static/config.json` | src/shared/config/config.ts |
|
||||||
|
| GET | `<processes>/tasks/?` + params.toString()` | modules/reviews/api/index.ts |
|
||||||
|
| GET | `file.attachment` | src/Components/Viewer/Panels/MeasurementDetails/MediaFiles_old.jsx |
|
||||||
|
| GET | `http://localhost:8000{path}` | src/Model/fakeServer/fakeServer.js |
|
||||||
|
| GET | `https://blooming-cove-51473.herokuapp.com/track/list` | src/Model/Store/trackingStore.js |
|
||||||
|
| GET | `https://blooming-cove-51473.herokuapp.com/track/notifications` | src/Model/Store/trackingStore.js |
|
||||||
|
| GET | `image` | src/Components/Viewer/Services/States/Stores/ViewerStates.ts |
|
||||||
|
| GET | `link` | src/Model/actions.js |
|
||||||
|
| GET | `url` | modules/account/ui/StorageCard/lib/file-utils.ts (+2) |
|
||||||
|
| POST | `url` | modules/reviews/api/index.ts |
|
||||||
|
| PUT | `url` | modules/reviews/api/index.ts |
|
||||||
|
| PATCH | `url` | modules/reviews/api/index.ts |
|
||||||
589
apps/django/openapi.yaml
Normal file
589
apps/django/openapi.yaml
Normal file
@ -0,0 +1,589 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
|
||||||
|
info:
|
||||||
|
title: Sarex Backend API
|
||||||
|
version: "1.1.12"
|
||||||
|
description: |
|
||||||
|
REST API сервиса **sarex-backend** — монолитное Django-приложение (проект
|
||||||
|
`config`, бизнес-логика в пакете `sarex`) на Django REST Framework. Отдаётся
|
||||||
|
через uWSGI (`config.wsgi:application`) на порту `8000`.
|
||||||
|
|
||||||
|
Документ описывает основную поверхность публичного API под префиксом `/api/`
|
||||||
|
и внутреннего API под префиксом `/internal/`. Маршрутизация собирается в
|
||||||
|
`config/urls.py` и включаемых `sarex/*/api/urls.py`. Многие ресурсы
|
||||||
|
зарегистрированы через DRF-роутеры (`SimpleRouter`/`DefaultRouter`), поэтому
|
||||||
|
поддерживают стандартный набор действий (list/create/retrieve/update/
|
||||||
|
partial_update/destroy). Конкретные схемы запросов/ответов в коде не
|
||||||
|
объявлены декларативно (используется `rest_framework.schemas.coreapi.AutoSchema`),
|
||||||
|
поэтому тела здесь описаны обобщённо.
|
||||||
|
|
||||||
|
### Аутентификация
|
||||||
|
Большинство эндпоинтов требуют аутентификации (DRF
|
||||||
|
`DEFAULT_PERMISSION_CLASSES = [IsAuthenticated]`). Поддерживаются несколько
|
||||||
|
механизмов (`DEFAULT_AUTHENTICATION_CLASSES`): Zitadel JWT, SimpleJWT
|
||||||
|
(`Authorization: Bearer <jwt>`, алгоритм `RS512`), Basic, Session,
|
||||||
|
RemoteUser. Токены выпускаются эндпоинтами `/api/token…`.
|
||||||
|
|
||||||
|
### Пагинация
|
||||||
|
По умолчанию используется `LimitOffsetPagination` (`PAGE_SIZE = 1000`).
|
||||||
|
Списочные ответы содержат `count`, `next`, `previous`, `results`.
|
||||||
|
|
||||||
|
### Замечание о полноте
|
||||||
|
Перечислены основные маршруты. Часть включаемых подмодулей
|
||||||
|
(`sarex/pg/api/*`, `sarex/mar/*`, `sarex/base/api/commons`) представлена
|
||||||
|
группами; детальные под-пути см. в соответствующих `urls.py`.
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://api.sarex.io
|
||||||
|
description: prod
|
||||||
|
- url: https://stage-api.sarex.io
|
||||||
|
description: stage
|
||||||
|
- url: /
|
||||||
|
description: contour (относительные пути)
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: auth
|
||||||
|
description: Аутентификация и JWT-токены
|
||||||
|
- name: base
|
||||||
|
description: Уведомления, workflows, модули, health
|
||||||
|
- name: core
|
||||||
|
description: Пользователи, компании, цели, миссии, медиа
|
||||||
|
- name: client
|
||||||
|
description: Клиентский дашборд и self-сервис
|
||||||
|
- name: analytics
|
||||||
|
description: Дашборды, метрики, виджеты
|
||||||
|
- name: map
|
||||||
|
description: Кадастр и заметки на карте
|
||||||
|
- name: pg
|
||||||
|
description: Облака точек, экспорт, измерения
|
||||||
|
- name: internal
|
||||||
|
description: Внутрикластерные вызовы
|
||||||
|
- name: system
|
||||||
|
description: Метрики и служебные эндпоинты
|
||||||
|
|
||||||
|
paths:
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Auth / tokens
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/login/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Вход пользователя (сессия)
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"401": { $ref: "#/components/responses/Unauthorized" }
|
||||||
|
/api/logout/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Выход пользователя
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/app-settings/:
|
||||||
|
get:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Настройки приложения
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/token/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Получить пару access/refresh токенов
|
||||||
|
security: []
|
||||||
|
requestBody: { $ref: "#/components/requestBodies/Generic" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
"401": { $ref: "#/components/responses/Unauthorized" }
|
||||||
|
/api/token/me:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Токен для текущего пользователя
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
/api/token/user/{pk}/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Токен для пользователя по id (из админки)
|
||||||
|
parameters: [ { $ref: "#/components/parameters/PkPath" } ]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
/api/token/jwks:
|
||||||
|
get:
|
||||||
|
tags: [auth]
|
||||||
|
summary: JWKS (набор публичных ключей)
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/token/refresh/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Обновить access-токен (ротация)
|
||||||
|
security: []
|
||||||
|
requestBody: { $ref: "#/components/requestBodies/Generic" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
/api/token/public/:
|
||||||
|
get:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Публичный ключ проверки JWT
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/auth/obtain/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Получить refresh-токен
|
||||||
|
security: []
|
||||||
|
requestBody: { $ref: "#/components/requestBodies/Generic" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
/api/auth/refresh/:
|
||||||
|
post:
|
||||||
|
tags: [auth]
|
||||||
|
summary: Обновить access-токен
|
||||||
|
security: []
|
||||||
|
requestBody: { $ref: "#/components/requestBodies/Generic" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/TokenPair" }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# System
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/metrics:
|
||||||
|
get:
|
||||||
|
tags: [system]
|
||||||
|
summary: Метрики Prometheus
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/health/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Health check
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Base
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/modules/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Список доступных модулей
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/update/notifications/:
|
||||||
|
post:
|
||||||
|
tags: [base]
|
||||||
|
summary: Массовое обновление уведомлений
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/workflows/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Список workflow
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/List" }
|
||||||
|
/api/workflows/{id}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/IdPath" } ]
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Workflow по id
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/notifications/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Список уведомлений
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/List" }
|
||||||
|
/api/notifications/{id}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/IdPath" } ]
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Уведомление по id
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/api/usernotifications/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Пользовательские уведомления
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/List" }
|
||||||
|
/api/commons/cs/:
|
||||||
|
get:
|
||||||
|
tags: [base]
|
||||||
|
summary: Справочник систем координат
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/List" }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Core — ресурсы DRF-роутера (CRUD)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/core/users/:
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Список пользователей
|
||||||
|
responses: { "200": { $ref: "#/components/responses/List" } }
|
||||||
|
post:
|
||||||
|
tags: [core]
|
||||||
|
summary: Создать пользователя
|
||||||
|
requestBody: { $ref: "#/components/requestBodies/Generic" }
|
||||||
|
responses: { "201": { $ref: "#/components/responses/Ok" } }
|
||||||
|
/api/core/users/{id}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/IdPath" } ]
|
||||||
|
get: { tags: [core], summary: Пользователь по id, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
put: { tags: [core], summary: Обновить пользователя, requestBody: { $ref: "#/components/requestBodies/Generic" }, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
patch: { tags: [core], summary: Частично обновить пользователя, requestBody: { $ref: "#/components/requestBodies/Generic" }, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
delete: { tags: [core], summary: Удалить пользователя, responses: { "204": { description: No Content } } }
|
||||||
|
/api/core/users/introspect:
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Интроспекция текущего пользователя
|
||||||
|
responses: { "200": { $ref: "#/components/responses/Ok" } }
|
||||||
|
/api/core/users/{pk}/introspect:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/PkPath" } ]
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Интроспекция пользователя (админ)
|
||||||
|
responses: { "200": { $ref: "#/components/responses/Ok" } }
|
||||||
|
/api/core/users/bulk/notifications/:
|
||||||
|
post:
|
||||||
|
tags: [core]
|
||||||
|
summary: Массовое обновление уведомлений пользователей
|
||||||
|
responses: { "200": { $ref: "#/components/responses/Ok" } }
|
||||||
|
/api/core/users_by_sa/:
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Пользователи по сервисному аккаунту
|
||||||
|
responses: { "200": { $ref: "#/components/responses/List" } }
|
||||||
|
/api/core/v2/users/:
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Упрощённый список пользователей (v2)
|
||||||
|
responses: { "200": { $ref: "#/components/responses/List" } }
|
||||||
|
/api/core/v3/users/:
|
||||||
|
get:
|
||||||
|
tags: [core]
|
||||||
|
summary: Оптимизированный список пользователей (v3)
|
||||||
|
responses: { "200": { $ref: "#/components/responses/List" } }
|
||||||
|
/api/core/companies/:
|
||||||
|
get: { tags: [core], summary: Список компаний, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
post: { tags: [core], summary: Создать компанию, requestBody: { $ref: "#/components/requestBodies/Generic" }, responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/companies/{id}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/IdPath" } ]
|
||||||
|
get: { tags: [core], summary: Компания по id, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/surfaces/:
|
||||||
|
get: { tags: [core], summary: Список поверхностей, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/mesh/:
|
||||||
|
get: { tags: [core], summary: Список mesh, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/multiple-surface/:
|
||||||
|
get: { tags: [core], summary: Множественные поверхности, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/state/:
|
||||||
|
get: { tags: [core], summary: Состояние приложения, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/polygons/:
|
||||||
|
get: { tags: [core], summary: Полигоны, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/media-notes/:
|
||||||
|
get: { tags: [core], summary: Медиа-заметки, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/media-notes-comments/:
|
||||||
|
get: { tags: [core], summary: Комментарии медиа-заметок, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/media-notes-attachments/:
|
||||||
|
get: { tags: [core], summary: Вложения медиа-заметок, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/media-folders/:
|
||||||
|
get: { tags: [core], summary: Папки медиа, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/target-links/:
|
||||||
|
get: { tags: [core], summary: Ссылки на цели, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/materials/:
|
||||||
|
get: { tags: [core], summary: Материалы, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/pointclouds/:
|
||||||
|
get: { tags: [core], summary: Облака точек, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/orthophotos/:
|
||||||
|
get: { tags: [core], summary: Ортофотопланы, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/videos/:
|
||||||
|
get: { tags: [core], summary: Видео, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/photos/:
|
||||||
|
get: { tags: [core], summary: Фото, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/panoramas/:
|
||||||
|
get: { tags: [core], summary: Панорамы, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/color-presets/:
|
||||||
|
get: { tags: [core], summary: Цветовые пресеты компании, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/missions/:
|
||||||
|
get: { tags: [core], summary: Список миссий, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/missions/import/:
|
||||||
|
get: { tags: [core], summary: Задачи импорта миссий, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/missions/import/create/:
|
||||||
|
post: { tags: [core], summary: Создать задачу импорта миссии, responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/targets/:
|
||||||
|
get: { tags: [core], summary: Список целей, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/targets/v2/:
|
||||||
|
get: { tags: [core], summary: Список целей (v2), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/targets/v3/:
|
||||||
|
get: { tags: [core], summary: Список целей (v3), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/targets/tree/:
|
||||||
|
get: { tags: [core], summary: Дерево целей, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/targets/pdm/:
|
||||||
|
get: { tags: [core], summary: Цели PDM, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/targets/{id}/missions/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/IdPath" } ]
|
||||||
|
get: { tags: [core], summary: Миссии цели, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/export/:
|
||||||
|
get: { tags: [core], summary: Экспорт данных, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/export/pointcloud/:
|
||||||
|
post: { tags: [core], summary: Экспорт региона из облака точек, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/contour/export/:
|
||||||
|
post: { tags: [core], summary: Экспорт контура, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/contour/exports/:
|
||||||
|
get: { tags: [core], summary: Список экспортов контура, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/uploads/streaming/:
|
||||||
|
post: { tags: [core], summary: Потоковая загрузка (создание), responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/uploads/streaming/{pk}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/PkStrPath" } ]
|
||||||
|
put: { tags: [core], summary: Догрузка чанка, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/storage-cleanup/:
|
||||||
|
post: { tags: [core], summary: Очистка хранилища, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/celery/stats:
|
||||||
|
get: { tags: [core], summary: Статистика Celery, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/celery/active:
|
||||||
|
get: { tags: [core], summary: Активные задачи Celery, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/celery/scheduled:
|
||||||
|
get: { tags: [core], summary: Запланированные задачи Celery, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/celery/{uuid}/details/:
|
||||||
|
parameters: [ { name: uuid, in: path, required: true, schema: { type: string } } ]
|
||||||
|
get: { tags: [core], summary: Детали задачи Celery, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/admin/users/:
|
||||||
|
get: { tags: [core], summary: Админ — пользователи, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/groups/:
|
||||||
|
get: { tags: [core], summary: Админ — группы, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/companies/:
|
||||||
|
get: { tags: [core], summary: Админ — компании, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/positions/:
|
||||||
|
get: { tags: [core], summary: Админ — должности, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/departments/:
|
||||||
|
get: { tags: [core], summary: Админ — подразделения, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/contractors/:
|
||||||
|
get: { tags: [core], summary: Админ — подрядчики, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/admin/permissions/:
|
||||||
|
get: { tags: [core], summary: Админ — права, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/service_accounts/:
|
||||||
|
get: { tags: [core], summary: Сервисные аккаунты, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/service-accounts/personalized/:
|
||||||
|
get: { tags: [core], summary: Персонализированный сервисный аккаунт, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/service-accounts/user/{user_id}/:
|
||||||
|
parameters: [ { name: user_id, in: path, required: true, schema: { type: integer } } ]
|
||||||
|
get: { tags: [core], summary: Сервисный аккаунт пользователя (v2), responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/c2s-comparisons/:
|
||||||
|
post: { tags: [core], summary: Создать c2s-сравнение, responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/permissions/:
|
||||||
|
get: { tags: [core], summary: Права (только чтение), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/units/:
|
||||||
|
get: { tags: [core], summary: Единицы измерения, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/upload/media-file/:
|
||||||
|
post: { tags: [core], summary: Загрузка медиа-файла, responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/mrpa/list/:
|
||||||
|
get: { tags: [core], summary: Список MRPA, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/core/mrpa/:
|
||||||
|
post: { tags: [core], summary: Создать MRPA, responses: { "201": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/core/mrpa/{pk}/:
|
||||||
|
parameters: [ { name: pk, in: path, required: true, schema: { type: string, format: uuid } } ]
|
||||||
|
get: { tags: [core], summary: MRPA по id, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Client
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/client/dashboard/targets/:
|
||||||
|
get: { tags: [client], summary: Дашборд — цели, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/client/dashboard/missions/:
|
||||||
|
get: { tags: [client], summary: Дашборд — миссии, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/client/dashboard/webcams/:
|
||||||
|
get: { tags: [client], summary: Дашборд — веб-камеры, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/client/settings/:
|
||||||
|
get: { tags: [client], summary: Настройки пользователя, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/client/settings/user/{user_id}/:
|
||||||
|
parameters: [ { name: user_id, in: path, required: true, schema: { type: integer } } ]
|
||||||
|
get: { tags: [client], summary: Настройки пользователя по id, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/client/self/:
|
||||||
|
get: { tags: [client], summary: Информация о себе, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/client/self/set_password/:
|
||||||
|
post: { tags: [client], summary: Смена пароля, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/client/uploads/:
|
||||||
|
get: { tags: [client], summary: Загрузки клиента, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/client/folders/:
|
||||||
|
get: { tags: [client], summary: Папки загрузок клиента, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Analytics
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/analytics/dashboards/:
|
||||||
|
get: { tags: [analytics], summary: Дашборды, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/widgets/:
|
||||||
|
get: { tags: [analytics], summary: Виджеты, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/metrics/:
|
||||||
|
get: { tags: [analytics], summary: Метрики, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/expressions/:
|
||||||
|
get: { tags: [analytics], summary: Выражения, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/folders/:
|
||||||
|
get: { tags: [analytics], summary: Папки метрик, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/filters/:
|
||||||
|
get: { tags: [analytics], summary: Фильтры, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/groups/:
|
||||||
|
get: { tags: [analytics], summary: Группы, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/values/:
|
||||||
|
get: { tags: [analytics], summary: Значения метрик, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/attributes/:
|
||||||
|
get: { tags: [analytics], summary: Атрибуты, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/attachments/:
|
||||||
|
get: { tags: [analytics], summary: Вложения дашбордов, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/analytics/reviews-service-feed/:
|
||||||
|
post: { tags: [analytics], summary: Приём событий из сервиса reviews, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/analytics/remarks-service-feed/:
|
||||||
|
post: { tags: [analytics], summary: Приём событий из сервиса remarks, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/analytics/tracking-service-feed/:
|
||||||
|
post: { tags: [analytics], summary: Приём событий трекинга, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Map
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/map/cadastre/point/:
|
||||||
|
get: { tags: [map], summary: Данные кадастра по точке, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/map/cadastre/export/:
|
||||||
|
post: { tags: [map], summary: Экспорт кадастровых данных, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/map/cadastre/wikimapia/redirect/:
|
||||||
|
get: { tags: [map], summary: Редирект Wikimapia, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/map/notes/:
|
||||||
|
get: { tags: [map], summary: Заметки на ортофото, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/map/notes/folders/:
|
||||||
|
get: { tags: [map], summary: Папки заметок на ортофото, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/ds/telecom/cadastre/:
|
||||||
|
get: { tags: [map], summary: Изображение кадастра (telecom), responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PG (облака точек / экспорт / измерения)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/api/pg/pointclouds/:
|
||||||
|
get: { tags: [pg], summary: Облака точек, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/pointclouds/{pointcloud}/measurements/:
|
||||||
|
parameters: [ { name: pointcloud, in: path, required: true, schema: { type: string } } ]
|
||||||
|
get: { tags: [pg], summary: Измерения облака точек, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/volume-dynamic/:
|
||||||
|
get: { tags: [pg], summary: Динамика объёмов, responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/pg/orthomosaicexport/:
|
||||||
|
get: { tags: [pg], summary: Экспорт ортомозаики (кастомный), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/exports/pointcloud/:
|
||||||
|
get: { tags: [pg], summary: Экспорты облаков точек, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/exports/orthomosaic/:
|
||||||
|
get: { tags: [pg], summary: Экспорты ортомозаики, responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/measurements/:
|
||||||
|
get: { tags: [pg], summary: Измерения (подмодуль), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/attachments/:
|
||||||
|
get: { tags: [pg], summary: Вложения (подмодуль), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/compare/:
|
||||||
|
get: { tags: [pg], summary: Сравнение (подмодуль), responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/pg/export/:
|
||||||
|
get: { tags: [pg], summary: Экспорт (подмодуль), responses: { "200": { $ref: "#/components/responses/Ok" } } }
|
||||||
|
/api/pg/pdf-overlays/:
|
||||||
|
get: { tags: [pg], summary: PDF-оверлеи (подмодуль), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
/api/pg/projects/:
|
||||||
|
get: { tags: [pg], summary: Проекты Metashape (при SERVER_USE_METASHAPE), responses: { "200": { $ref: "#/components/responses/List" } } }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
/internal/client/settings/{pk}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/PkPath" } ]
|
||||||
|
get:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Внутренние настройки клиента
|
||||||
|
responses: { "200": { $ref: "#/components/responses/Ok" } }
|
||||||
|
/internal/client/token/{pk}/:
|
||||||
|
parameters: [ { $ref: "#/components/parameters/PkPath" } ]
|
||||||
|
get:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Внутренний токен клиента
|
||||||
|
responses: { "200": { $ref: "#/components/responses/TokenPair" } }
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
IdPath:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: integer }
|
||||||
|
PkPath:
|
||||||
|
name: pk
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: integer }
|
||||||
|
PkStrPath:
|
||||||
|
name: pk
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
|
||||||
|
requestBodies:
|
||||||
|
Generic:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
|
||||||
|
responses:
|
||||||
|
Ok:
|
||||||
|
description: Успешный ответ
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
List:
|
||||||
|
description: Списочный ответ с пагинацией (LimitOffset)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/PaginatedList"
|
||||||
|
TokenPair:
|
||||||
|
description: Пара токенов
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/TokenPair"
|
||||||
|
Unauthorized:
|
||||||
|
description: Не аутентифицирован
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Error"
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
PaginatedList:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
count: { type: integer }
|
||||||
|
next: { type: string, nullable: true, format: uri }
|
||||||
|
previous: { type: string, nullable: true, format: uri }
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
TokenPair:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
access: { type: string }
|
||||||
|
refresh: { type: string }
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
detail: { type: string }
|
||||||
16
apps/document-link/.env.example
Normal file
16
apps/document-link/.env.example
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# document-link-frontend (Next.js)
|
||||||
|
#
|
||||||
|
# Внимание: текущий код фронтенда НЕ читает переменные окружения напрямую
|
||||||
|
# (в src/ нет обращений к process.env / NEXT_PUBLIC_*). Базовый URL API
|
||||||
|
# выбирается по window.location.hostname, а JWT зашит в коде (см. CONFIGURATION.md).
|
||||||
|
# Перечисленные ниже переменные — это конфигурационная поверхность, объявленная
|
||||||
|
# в Helm (.helm/values.yaml), и их следует использовать вместо хардкода.
|
||||||
|
|
||||||
|
# Публичный базовый хост API (сервис documentations), без схемы.
|
||||||
|
# Итоговый запрос: https://<NEXT_PUBLIC_API_BASE_URL>/documentations/api/v1/public/documents/public_link/<uuid>
|
||||||
|
# stage: stage-api.sarex.io, production: api.sarex.io
|
||||||
|
NEXT_PUBLIC_API_BASE_URL=stage-api.sarex.io
|
||||||
|
|
||||||
|
# JWT сервисного аккаунта для запроса публичной ссылки (Authorization: Bearer <jwt>).
|
||||||
|
# В инфраструктуре берётся из секрета documentations-publiclink-jwt-secret (ключ jwt).
|
||||||
|
NEXT_PUBLIC_API_TOKEN=
|
||||||
95
apps/document-link/CONFIGURATION.md
Normal file
95
apps/document-link/CONFIGURATION.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Конфигурация проекта document-link (document-link-frontend)
|
||||||
|
|
||||||
|
Документ описывает способы конфигурирования и все переменные окружения сервиса публичных ссылок на документы.
|
||||||
|
|
||||||
|
## Что это за сервис
|
||||||
|
|
||||||
|
`document-link-frontend` — микрофронтенд на **Next.js 13** (App Router, `src/app`), отдающий публичную страницу-карточку документа по ссылке вида `https://document-link.<env>.sarex.io/<uuid>`. По `uuid` фронтенд запрашивает метаданные документа у сервиса `documentations` и показывает название, автора, версию, размер, срок действия ссылки и кнопку скачивания (см. `ENDPOINTS.md`). Собственного бэкенда у сервиса нет — это чистый фронтенд, поэтому файлы вида `openapi.yaml` для него неприменимы.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
В отличие от бэкенд-сервисов, у фронтенда **нет разбора переменных окружения в коде**. На текущий момент:
|
||||||
|
|
||||||
|
- Базовый хост API выбирается в рантайме по `window.location.hostname` в `src/app/[uuid]/components/modal.tsx` (жёстко заданный `switch`), а не из переменной окружения;
|
||||||
|
- JWT для авторизации запроса **зашит в коде** (константа `fixedToken` в том же файле) — временное решение;
|
||||||
|
- Обращений к `process.env` / `NEXT_PUBLIC_*` в `src/` нет.
|
||||||
|
|
||||||
|
При этом конфигурационная поверхность **объявлена в инфраструктуре** (Helm-чарт репозитория и CI) в виде переменных `NEXT_PUBLIC_*` — их предполагается использовать вместо хардкода. Ниже описаны и код, и инфраструктурные объявления.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся значения |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (`npm run dev`) | Значения зашиты в коде; `apiBaseUrl` для `localhost` → `stage-api.sarex.io` |
|
||||||
|
| Локально (Docker / `docker-compose`) | `Dockerfile` (`node:18-alpine`, `next build`, `ENTRYPOINT npm start`, порт `3000`); `docker-compose.yaml` пробрасывает `8000:3000` |
|
||||||
|
| Kubernetes — репозиторный чарт | `.helm/values.yaml`: блок `services.frontend.envs` и `secretEnvs` (см. ниже) |
|
||||||
|
| Kubernetes — infra (этот репозиторий) | `apps/document-link/base` (kustomize) и `apps/document-link/{brusnika-stage,brusnika-prod}` (FluxCD `HelmRelease` поверх `universal-chart`) |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: `generic/common-ci` (`universal-pipeline`, ref `apps-business`), `SERVICE_NAME=document-link` |
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Объявлены в `.helm/values.yaml` исходного репозитория. **Важно:** текущий код фронтенда их не читает (см. раздел «Замечания»).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `NEXT_PUBLIC_API_BASE_URL` | string | `stage-api.sarex.io` (stage), `api.sarex.io` (production) | Базовый хост API (сервис `documentations`), без схемы |
|
||||||
|
| `NEXT_PUBLIC_API_TOKEN` | string (secret) | — | JWT сервисного аккаунта для `Authorization: Bearer <jwt>`. Берётся из секрета `documentations-publiclink-jwt-secret` (ключ `jwt`) |
|
||||||
|
|
||||||
|
## Базовые хосты API по окружениям
|
||||||
|
|
||||||
|
Логика `src/app/[uuid]/components/modal.tsx` (`switch` по `window.location.hostname`):
|
||||||
|
|
||||||
|
| Hostname фронтенда | `apiBaseUrl` |
|
||||||
|
| --- | --- |
|
||||||
|
| `localhost` | `stage-api.sarex.io` |
|
||||||
|
| `document-link.stage.sarex.io` | `stage-api.sarex.io` |
|
||||||
|
| `document-link.sarex.io` | `api.sarex.io` |
|
||||||
|
| прочее | не определён (ошибка в консоль, fallback `stage-api.sarex.io`) |
|
||||||
|
|
||||||
|
## Сборка и контейнер
|
||||||
|
|
||||||
|
| Параметр | Значение | Где задано |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Базовый образ | `node:18-alpine` | `Dockerfile` |
|
||||||
|
| Команда сборки | `npm i` → `npm run build` (`next build`) | `Dockerfile` |
|
||||||
|
| Entrypoint | `npm start` (`next start`) | `Dockerfile` |
|
||||||
|
| Порт приложения | `3000` | `Dockerfile` (`EXPOSE 3000`), `.helm/values.yaml` (`port._default: 3000`) |
|
||||||
|
| Образ (registry) | `cr.yandex/crp3ccidau046kdj8g9q/document-link-frontend` | `.helm/values.yaml`, infra `HelmRelease` |
|
||||||
|
|
||||||
|
## Деплой (infra: `apps/document-link`)
|
||||||
|
|
||||||
|
| Оверлей | Механизм | Особенности |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `base` | kustomize (`Deployment` + `Service` + `Namespace`) | namespace `document-link` c `istio-injection: enabled`; `Deployment/frontend` |
|
||||||
|
| `brusnika-stage` | FluxCD `HelmRelease` → `universal-chart` `0.1.7` | `replicaCount`: stage `1`; probes выключены |
|
||||||
|
| `brusnika-prod` | FluxCD `HelmRelease` → `universal-chart` `0.1.7` | `replicaCount`: preprod/production `3`; probes выключены |
|
||||||
|
| `yc-k8s-test` | kustomize (`../base`) | тестовый контур |
|
||||||
|
|
||||||
|
Порты сервиса: в `universal-chart` — `service.port 8080` → `targetPort 3000` (`portName: http`); в `base/service.yaml` — `port 80` → `targetPort 80`.
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`). Ключевые переменные: `SERVICE_NAME=document-link`, `DOCKERFILE_PATH=./Dockerfile`, `CI_TRIGGER_SOURCE=app`. Окружение переключается по ветке/тегу:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace | Chart |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `merge_request_event` | — | — | `ENABLE_BUILD_IMAGE=false` (только проверки) |
|
||||||
|
| ветка `stage` | `stage` | `documentations` | `document-link` `0.1.7`, `--build-arg ENV=stage` |
|
||||||
|
| ветка `master` | `preprod` | `document-link-preprod` | `document-link` `0.1.7`, `--build-arg ENV=preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `production` | `document-link-prod` | `document-link` `0.1.7`, `--build-arg ENV=prod` |
|
||||||
|
|
||||||
|
`HELM_SET_ARGS` пробрасывает в `universal-chart` образ (`services.frontend.image.name.<env>`), `global.env`, а также `commitSha`/`gitlabUri`/`gitlabJobUrl`/`owner`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **Env-переменные не используются кодом.** `NEXT_PUBLIC_API_BASE_URL` и `NEXT_PUBLIC_API_TOKEN` объявлены в `.helm/values.yaml`, но `src/` их не читает: базовый хост берётся из `switch` по `hostname`, а токен зашит константой `fixedToken`. Для корректной работы на разных стендах логику стоит перевести на `process.env.NEXT_PUBLIC_*` (и тогда `.env.example` станет рабочим шаблоном).
|
||||||
|
- **Зашитый JWT.** Константа `fixedToken` в `modal.tsx` — секрет в исходниках и с ограниченным сроком действия (`exp`). Должен приходить из секрета `documentations-publiclink-jwt-secret` через `NEXT_PUBLIC_API_TOKEN`.
|
||||||
|
- **Неизвестный hostname.** При домене, не входящем в `switch`, `apiBaseUrl` не задаётся явно (используется дефолт `stage-api.sarex.io`) — для новых стендов список нужно расширять.
|
||||||
|
- **Расхождение портов.** Приложение слушает `3000` (Dockerfile/helm `targetPort`), но `apps/document-link/base/deployment.yaml` объявляет `containerPort: 80`, а `base/service.yaml` — `port/targetPort 80`. В `universal-chart` (`HelmRelease`) — корректный `targetPort 3000`. Kustomize-`base` стоит выровнять на `3000`.
|
||||||
|
- **Многоступенчатый Dockerfile закомментирован.** Финальный `runner`-stage отключён — образ запускается из `builder` с полным `node_modules`; для прод-образа стоит включить slim-runner.
|
||||||
|
|
||||||
|
## Минимальный набор для запуска
|
||||||
|
|
||||||
|
- Локально: `npm i && npm run dev`, открыть `http://localhost:3000/<uuid>` (API — `stage-api.sarex.io`).
|
||||||
|
- Docker: `docker compose up` (порт `8000` → контейнер `3000`).
|
||||||
|
- В кластере фактически требуется рабочий JWT для сервиса `documentations` (сейчас — `fixedToken`; целевое — секрет `documentations-publiclink-jwt-secret`).
|
||||||
66
apps/document-link/ENDPOINTS.md
Normal file
66
apps/document-link/ENDPOINTS.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует document-link-frontend
|
||||||
|
|
||||||
|
Документ описывает HTTP-эндпоинты внешних сервисов, к которым обращается фронтенд публичных ссылок (`document-link-frontend`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Фронтенд загружает карточку документа по `uuid` из URL (`/<uuid>`). Запрос выполняется хуком `useSWR` в `src/app/[uuid]/components/modal.tsx` через нативный `fetch`. Базовый хост API выбирается в рантайме по `window.location.hostname`, итоговый URL = `https://<apiBaseUrl>` + путь эндпоинта. Скачивание файлов выполняется переходом браузера по ссылкам, которые возвращает сам API (`download_link`, `download_mrpas_link`).
|
||||||
|
|
||||||
|
Авторизация: заголовок `Authorization: Bearer <jwt>` (сейчас — зашитая константа `fixedToken`; целевое — `NEXT_PUBLIC_API_TOKEN` из секрета `documentations-publiclink-jwt-secret`). Запрос идёт с `credentials: "include"`.
|
||||||
|
|
||||||
|
## Базовые хосты по окружениям
|
||||||
|
|
||||||
|
Значения из `switch` по `window.location.hostname` в `modal.tsx`:
|
||||||
|
|
||||||
|
| Hostname фронтенда | `apiBaseUrl` (базовый хост API) |
|
||||||
|
| --- | --- |
|
||||||
|
| `localhost` | `https://stage-api.sarex.io` |
|
||||||
|
| `document-link.stage.sarex.io` | `https://stage-api.sarex.io` |
|
||||||
|
| `document-link.sarex.io` | `https://api.sarex.io` |
|
||||||
|
| прочее | не определён (fallback `https://stage-api.sarex.io`) |
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `documentations` — Сервис документации
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Публичная ссылка | GET | `/documentations/api/v1/public/documents/public_link/{uuid}` | Метаданные документа по публичной ссылке (`uuid`) |
|
||||||
|
|
||||||
|
Пример итогового URL: `https://stage-api.sarex.io/documentations/api/v1/public/documents/public_link/e602b98d-58a1-4ba3-8a89-84e5617b5aac`.
|
||||||
|
|
||||||
|
Ожидаемые поля ответа (используются во фронтенде):
|
||||||
|
|
||||||
|
| Поле ответа | Тип | Использование |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `name` | string | Название документа |
|
||||||
|
| `document_type` | string | Тип (иконка): `bim`/`bimv2`/`cloud`/`surface`/`workspace`/`pdf`/`deviation`/`c2s`/`c2c`/`abap`/`ksg`/`docx`/`xlsx`/`dxf`/`dwg`/… |
|
||||||
|
| `author` | string | Автор |
|
||||||
|
| `version` | string | Версия документа (скрывается для `workspace`/`folder`/`project`) |
|
||||||
|
| `size` | number | Размер в байтах (форматируется библиотекой `bytes`) |
|
||||||
|
| `download_token` | string | Токен скачивания |
|
||||||
|
| `download_link` | string (URL) | Прямая ссылка на скачивание файла |
|
||||||
|
| `download_mrpas_link` | string (URL) \| null | Ссылка на скачивание МЧД (опционально) |
|
||||||
|
| `expires_at` | string (datetime) \| null | Срок действия ссылки; `null` → «Неограничено» |
|
||||||
|
| `is_connector` | bool | Признак «файл > 5 Гб, требуется Sarex-коннектор» |
|
||||||
|
|
||||||
|
### Скачивание файлов (динамические ссылки)
|
||||||
|
|
||||||
|
Не отдельные эндпоинты реестра, а переход браузера по URL из ответа:
|
||||||
|
|
||||||
|
| Действие | Источник URL |
|
||||||
|
| --- | --- |
|
||||||
|
| Скачать файл/папку | `download_link` из ответа `public_link` |
|
||||||
|
| Скачать МЧД | `download_mrpas_link` из ответа `public_link` (если не `null`) |
|
||||||
|
|
||||||
|
При `is_connector = true` вместо прямого скачивания показывается предупреждение со ссылкой на [Sarex-коннектор](https://support.sarex.io/knowledge_base/item/347946).
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Статус ответа маппится в человекочитаемое сообщение (`modal.tsx`):
|
||||||
|
|
||||||
|
| Статус | Сообщение |
|
||||||
|
| --- | --- |
|
||||||
|
| `400`, `404` | «Ссылка не найдена» |
|
||||||
|
| `410` | «Время действия вашей ссылки истекло» |
|
||||||
|
| `500`, `503` | «Что-то пошло не так» |
|
||||||
229
apps/documentations/api-v2.CONFIGURATION.md
Normal file
229
apps/documentations/api-v2.CONFIGURATION.md
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
# Конфигурация проекта documentations-api-v2
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса **documentation-api-v2** (`pdm/documentation-api-v2`) — Go-сервис домена «documentations» (v2), отвечающий за диски, документы, бандлы, data source, страницы, публичные ссылки, workflow-обработку и подписи.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` через библиотеку [`github.com/kelseyhightower/envconfig`](https://github.com/kelseyhightower/envconfig) (функция `config.New()` → `envconfig.Process("", cfg)`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **Префикса нет** — переменные читаются под своими именами (напр. `API_ADDRESS`, `POSTGRES_ADDRESS`), имя задаётся тегом `envconfig:"..."`.
|
||||||
|
- **Вложенность не используется** — конфиг плоский. Параметры БД вынесены в встроенную структуру `gopg.Config` (`pkg/postgres/gopg/postgres.go`), но остаются на верхнем уровне переменных.
|
||||||
|
- **Дефолты** заданы тегом `default:"..."` только у части полей (см. таблицы). Поле без дефолта, которое не передали, получает нулевое значение Go (`""`, `0`, `false`) — жёсткой валидации «обязательности» у envconfig в этом коде нет, сервис стартует и с пустыми значениями.
|
||||||
|
- Переменная БД-сертификата имеет имя с дефисами `YC-PG-CERTIFICATE` (тег `envconfig:"YC-PG-CERTIFICATE"`).
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет. Единственный внешний файл — JSON-описание workflow-задач (`WORKFLOWS_CONFIG_FILEPATH`), разбираемый отдельно в `config/workflows.go` (`NewTasksExecutionConfigFromFilepath`); при ошибке чтения/парсинга сервис **паникует**.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса. `.env.template` — только шаблон; приложение **не загружает `.env` автоматически** (в коде нет dotenv) |
|
||||||
|
| Локально (docker-compose) | `make docker-compose` → `.docker/docker-compose.yml` с `env_file: .docker/.docker.env` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`, чарт-зависимость `universal-chart`: блоки `envs` (обычные значения) и `secretEnvs` (из k8s-секретов) сервиса `api` |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`) — переключение окружения/namespace, `HELM_SET_ARGS` |
|
||||||
|
|
||||||
|
Способы запуска процессов (`cmd/`):
|
||||||
|
|
||||||
|
| Бинарник | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `api_server` | `cmd/api_server/main.go` | Основной HTTP API (Fiber). Точка входа контейнера (`CMD ["./api_server"]`) |
|
||||||
|
| `migrate` (`migrations`) | `cmd/migrate/main.go` | Миграции БД (`go-pg-migrations`): `migrate` / `rollback`. Собирается как `./migrations` |
|
||||||
|
| `filestream_server` | `cmd/filestream_server/main.go` | Отдельный сервер потоковой отдачи файлов (в основном Dockerfile **не собирается**) |
|
||||||
|
|
||||||
|
Сборка образа (`.docker/api.Dockerfile`, тег `-tags migrate`) кладёт `api_server`, `migrations` и `.example.tasks_execution_config.json`. Миграции применяются автоматически при старте приложения (см. README), либо вручную через бинарник `migrations`.
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что значение в коде по умолчанию не задано (используется нулевое значение Go, если переменную не передать).
|
||||||
|
|
||||||
|
### App / окружение
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `APP_NAME` | string | `documentations-backend` | Имя приложения (Sentry `ServerName`) |
|
||||||
|
| `APP_VERSION` | string | `v1` | Версия (Sentry `Release`) |
|
||||||
|
| `ENVIRONMENT` | string | — | Окружение: `stage`/`preprod`/`production` (Sentry `Environment`) |
|
||||||
|
| `NAMESPACE` | string | — | k8s namespace, используется в логике дисков |
|
||||||
|
|
||||||
|
### HTTP-сервер
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `API_ADDRESS` | string | — | Адрес прослушивания Fiber. Читается **обоими** бинарниками (`api_server` и `filestream_server`) |
|
||||||
|
|
||||||
|
> `api_server` устанавливает большой `BodyLimit` (5 ТБ) и `ReadBufferSize` 96×4096; `filestream_server` — `BodyLimit` 64 МБ. Оба отдают `GET /ping` для проб k8s; `api_server` дополнительно отдаёт `GET /swagger/*`.
|
||||||
|
|
||||||
|
### Database (`gopg.Config`, `pkg/postgres/gopg/postgres.go`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS` | string | — | Хост PostgreSQL |
|
||||||
|
| `POSTGRES_PORT` | string | — | Порт PostgreSQL |
|
||||||
|
| `POSTGRES_USER` | string | — | Пользователь БД |
|
||||||
|
| `POSTGRES_DB` | string | — | Имя базы данных |
|
||||||
|
| `POSTGRES_PASSWORD` | string | — | Пароль пользователя БД |
|
||||||
|
| `POSTGRES_POOL_SIZE` | int | — | Размер пула соединений |
|
||||||
|
| `ENABLE_SSL` | bool | — | Подключение к БД по TLS (сертификат из `YC-PG-CERTIFICATE`) |
|
||||||
|
| `ENABLE_SQL_QUERY` | bool | — | Логирование SQL-запросов |
|
||||||
|
| `YC-PG-CERTIFICATE` | string (PEM) | — | CA-сертификат PostgreSQL. Обязателен при `ENABLE_SSL=1` |
|
||||||
|
|
||||||
|
### Аутентификация и публичные ссылки
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `PUBLIC_KEY` | string (PEM) | — | Публичный RSA-ключ (PKIX) для проверки JWT sarex-backend |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_SECRET` | string | — | HMAC-секрет JWT для публичных/временных ссылок на документы |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_EXPIRATION_MINUTES` | uint8 | — | TTL публичной ссылки, минуты |
|
||||||
|
| `PUBLIC_LINK_HOST` | string | — | Базовый хост генерируемых публичных ссылок |
|
||||||
|
|
||||||
|
### Django / IAM (`pkg/django`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DJANGO_HOST` | string | — | Базовый URL Django/монолита (пользователи, компании, сервис-аккаунты) |
|
||||||
|
| `DJANGO_BASIC_AUTH` | string (base64) | — | Basic-auth `base64(login:password)` для Django и клиента flows |
|
||||||
|
| `DJANGO_ORIGINATOR` | string | — | Идентификатор источника (`docs_stage`/`docs_preprod`/`docs_prod`) |
|
||||||
|
|
||||||
|
### Внешние сервисы (базовые URL клиентов)
|
||||||
|
|
||||||
|
Каждый клиент (`pkg/clients/*`) создаётся с `SetBaseURL(<URL>)` и ретраями. Подробнее по путям — см. `api-v2.ENDPOINTS.md`.
|
||||||
|
|
||||||
|
| Переменная | Тип | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DOCUMENTATION_URL` | string | Self-URL сервиса, подставляется в workflow-задачи |
|
||||||
|
| `WORKFLOW_URL` | string | Сервис workflows (запуск обработки) |
|
||||||
|
| `WORKSPACE_URL` | string | Сервис workspaces |
|
||||||
|
| `WORKSPACE_V2_EXTERNAL_URL` | string | Внешний URL workspaces v2 |
|
||||||
|
| `WORKSPACE_BUNDLE_VERSION` | string | Версия бандла для интеграции с workspaces |
|
||||||
|
| `MARKS_PROCESSING_URL` | string | Сервис PDF-маркировок (marks) |
|
||||||
|
| `BIM_API_URL` | string | BIM API v1 |
|
||||||
|
| `BIM_API_V2_URL` | string | BIM API v2 (bim-core) |
|
||||||
|
| `BIM_API_URL_EXTERNAL` | string | Внешний URL BIM API |
|
||||||
|
| `SYSTEM_LOG_URL` | string | Сервис журналирования (system-log) |
|
||||||
|
| `FLOWS_URL` | string | Сервис flows |
|
||||||
|
| `FILE_URL_EXTERNAL` | string | Внешний URL для отдачи файлов |
|
||||||
|
|
||||||
|
### S3 / MinIO (`pkg/s3/minio`)
|
||||||
|
|
||||||
|
Клиент инициализируется только при `ENABLE_S3=1` (иначе `api_server` работает без S3).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_S3` | bool | — | Включить инициализацию S3-клиента |
|
||||||
|
| `S3_SERVICE_ACCOUNT` | string | — | Путь к JSON сервис-аккаунта Yandex S3 |
|
||||||
|
| `S3_SERVICE_ACCOUNT_STR` | string | — | Альтернатива: JSON сервис-аккаунта строкой |
|
||||||
|
|
||||||
|
### BIM-логика
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USE_BIMV1_FOR_BIMV2` | bool | — | Использовать BIM v1 API вместо v2 в пайплайне документов |
|
||||||
|
| `LAST_MASTER_BIM` | int | — | Граничный id для маршрутизации BIM master |
|
||||||
|
| `LAST_SLAVE_1_BIM` | int | — | Граничный id для маршрутизации BIM slave 1 |
|
||||||
|
|
||||||
|
### Кеш и файловый стример
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `READ_WRITE_TIMEOUT_FILE_STREAM` | duration | — | Таймаут чтения/записи файлового стримера (напр. `6h`) |
|
||||||
|
| `CACHE_DEFAULT_EXPIRATION` | duration | — | TTL кеша (напр. `60s`) |
|
||||||
|
| `CACHE_CLEANUP_INTERVAL` | duration | — | Интервал очистки кеша |
|
||||||
|
| `USE_CACHE_IN_FILE_STREAMER` | bool | — | Включить кеш в файловом стримере |
|
||||||
|
|
||||||
|
### Workflow-задачи
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `WORKFLOWS_CONFIG_FILEPATH` | string | `.example.tasks_execution_config.json` | Путь к JSON-описанию задач (`config/workflows.go`); при ошибке — паника |
|
||||||
|
| `WORKFLOWS_IMAGES_VERSION` | string | — | Тег образов задач (`develop`/`master`) |
|
||||||
|
| `CONTAINER_REGISTRY` | string | `cr.yandex/crp3ccidau046kdj8g9q` | Реестр образов задач |
|
||||||
|
| `IS_CONVERTED_PDF_UPLOADING_TO_S3` | bool | `true` | Загружать сконвертированный PDF в S3 |
|
||||||
|
|
||||||
|
### Наблюдаемость: Sentry и OpenTelemetry
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `SENTRY_DSN` | string | — | DSN Sentry |
|
||||||
|
| `SENTRY_DEBUG` | bool | — | Debug-режим Sentry |
|
||||||
|
| `ENABLE_OBSERVABILITY` | bool | — | Включить slog-хендлер observability и трассировку запросов БД |
|
||||||
|
| `OBSERVABILITY_COLLECTOR_ENDPOINT` | string | — | Endpoint OTLP-коллектора логов |
|
||||||
|
| `TRACER_USE` | bool | `false` | Включить OTEL-трейсинг Fiber |
|
||||||
|
| `TRACER_HOST` | string | `localhost:4317` | Адрес OTLP-коллектора трейсов |
|
||||||
|
| `TRACER_USE_INSECURE` | bool | `true` | Подключение к коллектору без TLS |
|
||||||
|
| `SERVICE_NAME` | string | `documentations-api-v2` | Имя сервиса в трейсах |
|
||||||
|
| `TRACER_LOGGER_NAME` | string | `tracer_logger` | Имя OTEL-логгера |
|
||||||
|
|
||||||
|
## Переменные инфраструктуры и сборки
|
||||||
|
|
||||||
|
Не читаются кодом приложения, но участвуют в запуске/сборке/деплое.
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `API_FILESTREAM_ADDRESS` | `.env.template`, `.docker.env` | Присутствует в шаблонах, но **кодом не читается** (см. Замечания) |
|
||||||
|
| `POSTGRES_EXTERNAL_PORT` | `.docker/docker-compose.yml` | Внешний порт проброса контейнера Postgres |
|
||||||
|
| `API_VERSION` | `.docker/docker-compose.yml` | Тег образа `api` (по умолчанию `local`) |
|
||||||
|
| `GITLAB_CREDENTIALS` | `.docker/api.Dockerfile` (build-arg) | Доступ к приватному GitLab при `go build` |
|
||||||
|
| `CI_COMMIT_SHORT_SHA` | `.docker/api.Dockerfile` (build-arg), CI | Идентификатор сборки |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Чарт — обёртка над зависимостью `universal-chart` (`oci://.../charts`, версия `0.1.7`). Сервис `api`: `deployment` (реплики, ресурсы, пробы `GET /ping:8080`), `service` (ClusterIP `80 → 8080`), `image`, `volumes`.
|
||||||
|
|
||||||
|
Смонтированные тома:
|
||||||
|
|
||||||
|
- Секрет `documentations-yc-s3` → `/etc/sarex/yc-s3-storage` (на него указывает `S3_SERVICE_ACCOUNT`);
|
||||||
|
- ConfigMap `tasks-execution-config-documentation-v2` → `/etc/app/tasks_execution_config.json` (на него указывает `WORKFLOWS_CONFIG_FILEPATH` в k8s).
|
||||||
|
|
||||||
|
Обычные значения (`envs`) задают те же переменные `APP_NAME`, `API_ADDRESS` (`0.0.0.0:8080`), `ENVIRONMENT`, `NAMESPACE`, URL внешних сервисов, `ENABLE_SSL=1`, `ENABLE_S3=1`, флаги трассировки и т.п. — с разбивкой по окружениям (`_default`/`stage`/`preprod`/`production`).
|
||||||
|
|
||||||
|
Значения из секретов (`secretEnvs`, монтируются через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`_default`) | Секрет (`preprod`) | Ключ |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `POSTGRES_USER` | `documentations-postgresql-secret` | `ya-pg-secret` | `username` |
|
||||||
|
| `POSTGRES_ADDRESS` | `documentations-postgresql-secret` | `ya-pg-secret` | `host` |
|
||||||
|
| `POSTGRES_PORT` | `documentations-postgresql-secret` | `ya-pg-secret` | `port` |
|
||||||
|
| `POSTGRES_DB` | `documentations-postgresql-secret` | `ya-pg-secret` | `database` |
|
||||||
|
| `POSTGRES_PASSWORD` | `documentations-postgresql-secret` | `ya-pg-secret` | `password` |
|
||||||
|
| `YC-PG-CERTIFICATE` | `documentations-postgresql-secret` (`ca.crt`) | `yc-pg-certificate` (`certificate`) | см. столбцы |
|
||||||
|
| `DJANGO_BASIC_AUTH` | `django-auth` | — | `key` |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_SECRET` | `yc-jwt-secret` | — | `secret` |
|
||||||
|
| `PUBLIC_KEY` | `public-key` | — | `key` |
|
||||||
|
|
||||||
|
## Переменные в 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` | `documentations` | `0.0.1-stage` |
|
||||||
|
| ветка `master` | `preprod` | `documentations-preprod` | `0.0.1-preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `0.0.1-prod` |
|
||||||
|
| `merge_request_event` | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||||||
|
|
||||||
|
Ключевые переменные: `SERVICE_NAME=documentations-v2`, `RELEASE_NAME`/`CHART_NAME=documentations-v2`, `DOCKERFILE_PATH=.docker/api.Dockerfile`, `HELM_SET_ARGS` (`--set universal-chart.global.env=…`, `--set universal-chart.services.api.image.name.<env>=${IMAGE_NAME}`), флаги `ENABLE_LINTER`/`ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_DEPLOY`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **Нет обязательности полей.** `envconfig` в этом коде не помечает поля как required — при отсутствии переменной берётся нулевое значение Go. Пустые критичные значения (адрес БД, `PUBLIC_KEY`) приведут к ошибке уже в рантайме (падение при `db.Ping`, отказ проверки JWT), а не на этапе разбора конфига.
|
||||||
|
- **`API_FILESTREAM_ADDRESS` не читается.** Оба сервера слушают `API_ADDRESS`; отдельной переменной для порта файлового стримера в коде нет.
|
||||||
|
- **Опечатка `POSTGRES_POLL_SIZE`.** В `.env.template`/`.docker.env` встречается `POSTGRES_POLL_SIZE`; код читает `POSTGRES_POOL_SIZE` (в helm имя корректное). Значение с опечаткой не подхватывается.
|
||||||
|
- **`YC-PG-CERTIFICATE`** — имя с дефисами, читается через явный тег `envconfig`. В `.helm` для preprod монтируется из отдельного секрета `yc-pg-certificate` (ключ `certificate`).
|
||||||
|
- **Файл workflow-задач обязателен по существу.** Если файл по пути `WORKFLOWS_CONFIG_FILEPATH` отсутствует или не парсится — `config.NewTasksExecutionConfigFromFilepath` вызывает `panic`. Локально нужен `.example.tasks_execution_config.json`, в k8s — том ConfigMap.
|
||||||
|
- **S3 опционален.** При `ENABLE_S3=0` S3-клиент не создаётся; хендлеры, работающие с хранилищем, будут получать `nil`-хранилище.
|
||||||
|
- **Автомиграции при старте.** Приложение накатывает миграции автоматически (см. README); ручной прогон — бинарником `migrations migrate`/`migrations rollback`.
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Postgres поднимается через `make docker-compose` (образ `timescale/timescaledb-ha:pg13`, инициализация расширений uuid/ltree из `.docker/install-uuid-ltree.sql`). Приложение — бинарник `./api_server`. Минимально задать:
|
||||||
|
|
||||||
|
- `API_ADDRESS` (напр. `localhost:8000`);
|
||||||
|
- `POSTGRES_ADDRESS`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_DB`, `POSTGRES_PASSWORD`, `POSTGRES_POOL_SIZE`, `ENABLE_SSL=0`, `ENABLE_SQL_QUERY`;
|
||||||
|
- `PUBLIC_KEY` (для проверки JWT sarex-backend), `DOCUMENT_PUBLIC_LINK_JWT_SECRET`;
|
||||||
|
- `WORKFLOWS_CONFIG_FILEPATH` с существующим JSON (по умолчанию `.example.tasks_execution_config.json`);
|
||||||
|
- `ENABLE_S3=0`, `TRACER_USE=false`, `ENABLE_OBSERVABILITY=0` — чтобы не поднимать S3/OTEL локально;
|
||||||
|
- URL внешних сервисов (`DJANGO_HOST`, `WORKFLOW_URL`, `WORKSPACE_URL`, `BIM_API_URL`, `BIM_API_V2_URL`, `MARKS_PROCESSING_URL`, `SYSTEM_LOG_URL`, `FLOWS_URL`) — по мере необходимости для соответствующих сценариев.
|
||||||
|
|
||||||
|
Готовые значения-примеры приведены в `api-v2.env.example` (с учётом замечаний выше).
|
||||||
88
apps/documentations/api-v2.ENDPOINTS.md
Normal file
88
apps/documentations/api-v2.ENDPOINTS.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует documentations-api-v2
|
||||||
|
|
||||||
|
Документ описывает HTTP-эндпоинты внешних сервисов, к которым обращается Go-сервис **documentation-api-v2** (`pdm/documentation-api-v2`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Внешние вызовы выполняются типизированными клиентами в `pkg/clients/*` и `pkg/django`. Каждый клиент строится на [`github.com/go-resty/resty/v2`](https://github.com/go-resty/resty) с `SetBaseURL(<URL>)`, ретраями и (опционально) OTEL-трассировкой. Базовый URL берётся из переменной окружения (см. `api-v2.CONFIGURATION.md`); итоговый URL = `<базовый URL>` + путь, указанный в коде метода клиента.
|
||||||
|
|
||||||
|
Клиенты создаются в `internal/api/httpserver/server.go` (`App.Run`) и передаются в репозитории/юзкейсы. Аутентификация к внешним сервисам:
|
||||||
|
|
||||||
|
- **Django** и **flows** используют Basic-auth из `DJANGO_BASIC_AUTH`;
|
||||||
|
- **workspace** (`Archive`) пробрасывает пользовательский Bearer-токен (`SetAuthToken`);
|
||||||
|
- прочие внутренние сервисы вызываются по кластерным адресам без явной авторизации на уровне клиента.
|
||||||
|
|
||||||
|
## Базовые адреса по сервисам
|
||||||
|
|
||||||
|
| Клиент (`pkg/...`) | Переменная базового URL | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `django` | `DJANGO_HOST` | Монолит/IAM: пользователи, компании, сервис-аккаунты, настройки |
|
||||||
|
| `clients/workspace` | `WORKSPACE_URL` | Сервис рабочих областей |
|
||||||
|
| `clients/workflows` | `WORKFLOW_URL` | Запуск workflow-обработки |
|
||||||
|
| `clients/bimv1` | `BIM_API_URL` | BIM API v1 |
|
||||||
|
| `clients/bimv2` | `BIM_API_V2_URL` | BIM API v2 (bim-core) |
|
||||||
|
| `clients/flows` | `FLOWS_URL` | Сервис flows (процессы) |
|
||||||
|
| `clients/marks` | `MARKS_PROCESSING_URL` | Сервис PDF-маркировок |
|
||||||
|
| `clients/system_log` | `SYSTEM_LOG_URL` | Сервис журналирования |
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `django` — монолит/IAM (`pkg/django`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `api/client/settings/` | Клиентские настройки |
|
||||||
|
| GET | `/api/core/companies/` | Список компаний |
|
||||||
|
| GET | `/api/core/users/` | Список пользователей |
|
||||||
|
| GET | `api/core/users/{user_id}/introspect` | Интроспекция пользователя |
|
||||||
|
| GET | `/api/core/service_accounts/` | Сервис-аккаунты |
|
||||||
|
| GET | `/api/core/service-accounts/personalized/` | Персонализированные сервис-аккаунты |
|
||||||
|
|
||||||
|
### `workspace` — рабочие области (`pkg/clients/workspace`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `internal/v2/workspaces` | Создать рабочую область |
|
||||||
|
| DELETE | `internal/v2/documents/{document_ids}` | Удалить документы из рабочих областей; возвращает id опустевших областей |
|
||||||
|
| POST | `api/v1/workspaces/{workspace_id}/archive` | Архивировать рабочую область (с пользовательским Bearer-токеном) |
|
||||||
|
|
||||||
|
### `workflows` — workflow-обработка (`pkg/clients/workflows`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `internal/v1/companies/{company_id}/workflows` | Создать/запустить workflow для компании |
|
||||||
|
|
||||||
|
### `bimv1` — BIM API v1 (`pkg/clients/bimv1`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/internal/v1/targets/{target_id}/bims-pdm` | Зарегистрировать новый BIM (v1) |
|
||||||
|
| POST | `/internal/v1/targets/{target_id}/bims-v2-pdm` | Зарегистрировать новый BIM (v2) |
|
||||||
|
|
||||||
|
### `bimv2` — BIM API v2 (`pkg/clients/bimv2`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/internal/v1/projects/{project_id}/bims` | Создать BIM в проекте |
|
||||||
|
|
||||||
|
### `flows` — процессы (`pkg/clients/flows`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `api/v1/documents/?full=true&document_ids={id}` | Документы процессов по id |
|
||||||
|
|
||||||
|
### `marks` — PDF-маркировки (`pkg/clients/marks`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/api/v1/marks/{bundle_id}` | Массовое создание маркировок для бандла |
|
||||||
|
|
||||||
|
### `system_log` — журналирование (`pkg/clients/system_log`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/api/v0/system_log` | Отправка пакета записей журнала |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Клиенты проверяют HTTP-статус ответа и при коде, отличном от ожидаемого (обычно `200`), оборачивают ошибку через `github.com/rotisserie/eris` с указанием имени метода клиента (напр. `bimapi.addNewBim: invalid response code %d expected 200`). Транспортные ошибки resty также оборачиваются `eris.Wrap`. Настроены ретраи (напр. клиент workspace — 5 попыток с паузой 1 c, таймаут 10 c).
|
||||||
87
apps/documentations/api-v2.env.example
Normal file
87
apps/documentations/api-v2.env.example
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# documentations-api-v2 (documentation-api-v2) — пример переменных окружения
|
||||||
|
# Разбор: github.com/kelseyhightower/envconfig, config/config.go + pkg/postgres/gopg/postgres.go
|
||||||
|
# Префикса у переменных нет; вложенность не используется. Дефолт указан там, где он задан в коде.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- App ---------------------------------------------------------------------
|
||||||
|
APP_NAME=documentations-backend # default: documentations-backend
|
||||||
|
APP_VERSION=v1 # default: v1; используется как Sentry Release
|
||||||
|
ENVIRONMENT=stage # stage/preprod/production; идёт в Sentry Environment
|
||||||
|
NAMESPACE=documentations # k8s namespace, используется в бизнес-логике дисков
|
||||||
|
|
||||||
|
# --- HTTP-сервер -------------------------------------------------------------
|
||||||
|
API_ADDRESS=localhost:8000 # адрес прослушивания Fiber (и api_server, и filestream_server)
|
||||||
|
# API_FILESTREAM_ADDRESS=localhost:8050 # присутствует в шаблоне, но КОДОМ НЕ ЧИТАЕТСЯ (см. Замечания)
|
||||||
|
|
||||||
|
# --- PostgreSQL (pkg/postgres/gopg) ------------------------------------------
|
||||||
|
POSTGRES_ADDRESS=localhost
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_DB=postgres
|
||||||
|
POSTGRES_USER=user
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
POSTGRES_POOL_SIZE=1 # в helm: POSTGRES_POOL_SIZE (в шаблоне встречается опечатка POSTGRES_POLL_SIZE — не читается)
|
||||||
|
ENABLE_SSL=0 # 1 → TLS к БД по сертификату из YC-PG-CERTIFICATE
|
||||||
|
ENABLE_SQL_QUERY=1 # логирование SQL-запросов
|
||||||
|
YC-PG-CERTIFICATE="" # PEM CA-сертификат PostgreSQL (имя с дефисами, читается через envconfig-тег)
|
||||||
|
# POSTGRES_EXTERNAL_PORT=6432 # только для docker-compose, приложением не читается
|
||||||
|
|
||||||
|
# --- Django / IAM (pkg/django) -----------------------------------------------
|
||||||
|
DJANGO_HOST=https://stage.sarex.io
|
||||||
|
DJANGO_BASIC_AUTH= # base64(login:password) для Basic-auth в Django и flows
|
||||||
|
DJANGO_ORIGINATOR=docs_stage # идентификатор источника для system-log/Django
|
||||||
|
|
||||||
|
# --- Внешние сервисы (base URL клиентов) -------------------------------------
|
||||||
|
DOCUMENTATION_URL=http://api-v2-service.documentations/ # self-URL, подставляется в workflow-задачи
|
||||||
|
WORKFLOW_URL=http://workflows-api-service.platform:8000/
|
||||||
|
WORKSPACE_URL=http://workspaces-backend-service.platform:8000/
|
||||||
|
WORKSPACE_V2_EXTERNAL_URL=https://stage.sarex.io/workspaces-v2/
|
||||||
|
WORKSPACE_BUNDLE_VERSION=v1
|
||||||
|
MARKS_PROCESSING_URL=http://marks-service.documentations:8000
|
||||||
|
BIM_API_URL=http://bim-api-service.bim-api-stage/
|
||||||
|
BIM_API_V2_URL=http://bim-core-api.platform.svc.cluster.local:8000/
|
||||||
|
BIM_API_URL_EXTERNAL=https://stage-api.sarex.io/bim
|
||||||
|
SYSTEM_LOG_URL=http://system-log-api-service.platform:80
|
||||||
|
FLOWS_URL=
|
||||||
|
FILE_URL_EXTERNAL= # внешний URL для отдачи файлов
|
||||||
|
|
||||||
|
# --- S3 / MinIO (pkg/s3/minio) -----------------------------------------------
|
||||||
|
ENABLE_S3=0 # 1 → инициализировать S3-клиент
|
||||||
|
S3_SERVICE_ACCOUNT=/etc/sarex/yc-s3-storage/yc-s3-service-account.json # путь к JSON сервис-аккаунта
|
||||||
|
S3_SERVICE_ACCOUNT_STR= # альтернатива: JSON сервис-аккаунта строкой
|
||||||
|
|
||||||
|
# --- Публичные ссылки на документы -------------------------------------------
|
||||||
|
PUBLIC_LINK_HOST= # хост генерируемых публичных ссылок
|
||||||
|
DOCUMENT_PUBLIC_LINK_JWT_SECRET="" # HMAC-секрет JWT публичной ссылки
|
||||||
|
DOCUMENT_PUBLIC_LINK_JWT_EXPIRATION_MINUTES=5 # TTL публичной ссылки (uint8)
|
||||||
|
|
||||||
|
# --- Аутентификация ----------------------------------------------------------
|
||||||
|
PUBLIC_KEY="" # PKIX RSA public key (PEM) для проверки JWT sarex-backend
|
||||||
|
|
||||||
|
# --- BIM-логика --------------------------------------------------------------
|
||||||
|
USE_BIMV1_FOR_BIMV2=0 # использовать BIM v1 API вместо v2
|
||||||
|
LAST_MASTER_BIM=0 # граничные id для маршрутизации BIM-запросов
|
||||||
|
LAST_SLAVE_1_BIM=0
|
||||||
|
|
||||||
|
# --- Кеш / файловый стример --------------------------------------------------
|
||||||
|
READ_WRITE_TIMEOUT_FILE_STREAM=6h # Go duration
|
||||||
|
CACHE_DEFAULT_EXPIRATION=60s # Go duration
|
||||||
|
CACHE_CLEANUP_INTERVAL=60s # Go duration
|
||||||
|
USE_CACHE_IN_FILE_STREAMER=1 # bool
|
||||||
|
|
||||||
|
# --- Workflow-задачи ---------------------------------------------------------
|
||||||
|
WORKFLOWS_IMAGES_VERSION=develop # тег образов задач (develop/master)
|
||||||
|
WORKFLOWS_CONFIG_FILEPATH=.example.tasks_execution_config.json # default; в k8s — /etc/app/tasks_execution_config.json
|
||||||
|
CONTAINER_REGISTRY=cr.yandex/crp3ccidau046kdj8g9q # default; реестр образов задач
|
||||||
|
IS_CONVERTED_PDF_UPLOADING_TO_S3=true # default: true
|
||||||
|
|
||||||
|
# --- Наблюдаемость: Sentry + OpenTelemetry -----------------------------------
|
||||||
|
SENTRY_DSN=""
|
||||||
|
SENTRY_DEBUG=false
|
||||||
|
ENABLE_OBSERVABILITY=0 # bool; включает slog-хендлер и трассировку в БД
|
||||||
|
OBSERVABILITY_COLLECTOR_ENDPOINT=0 # endpoint OTLP-коллектора логов
|
||||||
|
TRACER_USE=false # default: false; включить OTEL-трейсинг Fiber
|
||||||
|
TRACER_HOST=localhost:4317 # default: localhost:4317
|
||||||
|
TRACER_USE_INSECURE=true # default: true
|
||||||
|
SERVICE_NAME=documentations-api-v2 # default: documentations-api-v2
|
||||||
|
TRACER_LOGGER_NAME=tracer_logger # default: tracer_logger
|
||||||
1258
apps/documentations/api-v2.openapi.yaml
Normal file
1258
apps/documentations/api-v2.openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
345
apps/documentations/api.CONFIGURATION.md
Normal file
345
apps/documentations/api.CONFIGURATION.md
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
# Конфигурация проекта documentations-api
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса документаций (`documentation-api`). Репозиторий собирает **два бинарника/образа**, разворачиваемых в неймспейсе `documentations`:
|
||||||
|
|
||||||
|
| Бинарник | Точка входа | Образ | Deployment | Назначение |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **API** | `cmd/api` | `documentations` (`cr.yandex/.../documentations-api`) | `documentations-api` | Основной REST-API: диски, документы, бандлы, права, воркспейсы, штампы, публичные ссылки и т. д. |
|
||||||
|
| **Filestream** | `cmd/filestreamer` | `documentations-api-files` (`.../documentations-filestream`) | `documentations-filestream` | Потоковая отдача/приём файлов из S3 (скачивание документов и бандлов, догрузка частей, gzip/range). |
|
||||||
|
|
||||||
|
Оба бинарника используют **одну и ту же структуру конфигурации** (`config.Config`) — различия только в том, какие поля реально задействуются (см. раздел «Различия api и filestream»).
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` через библиотеку [`github.com/kelseyhightower/envconfig`](https://github.com/kelseyhightower/envconfig) (функция `config.FromEnv()` → `envconfig.Process("", &cfg)`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **Префикса нет** (в `envconfig.Process` передаётся пустая строка) — имена переменных плоские, задаются тегом `envconfig:"..."` у каждого поля структуры `Config`;
|
||||||
|
- **Вложенности нет** — двойных подчёркиваний/секций, как в pydantic, здесь не используется;
|
||||||
|
- **Значения по умолчанию** задаются тегом `default:"..."` прямо в структуре (например `USE_BIM_INSERTER default:"true"`). Поля без `default` и без значения в окружении получают нулевое значение типа (`""`, `0`, `false`, `nil`) — то есть формально **обязательных полей с ошибкой старта у envconfig нет**; отсутствующая переменная просто становится «пустой», а несостоятельность конфигурации всплывает позже в рантайме (например, невозможность подключиться к БД/S3);
|
||||||
|
- **Отдельного конфиг-файла (yaml/toml) у приложения нет.** Приложение **не загружает `.env` автоматически** — переменные должны быть в окружении процесса (в контейнере их проставляет Helm, локально — вручную или через `docker-compose --env-file`);
|
||||||
|
- Единственный внешний файл конфигурации — `WORKFLOWS_CONFIG_FILEPATH` (JSON с параметрами запуска задач обработки, см. `.example.tasks_execution_config.json`), читается `workflow.NewTasksExecutionConfigFromFilepath` при старте api.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса. Готовый шаблон — `.docker/.env` |
|
||||||
|
| Локально (контейнеры) | `.docker/docker-compose.yml` + `make docker`: значения из `.docker/.env` и `.docker/.docker.env` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml` (universal-chart): блоки `services.api.envs`/`secretEnvs` и `services.filestream.envs`/`secretEnvs` |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: `workflow.rules` (окружение по ветке/тегу) и `HELM_SET_ARGS` |
|
||||||
|
|
||||||
|
Способы запуска процессов:
|
||||||
|
|
||||||
|
| Команда | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `api` (`make api` / `make run-api-dev`) | `cmd/api` | Основной REST-API |
|
||||||
|
| `filestreamer` (`make file_api` / `make run-filestreamer-dev`) | `cmd/filestreamer` | Файловый стример |
|
||||||
|
| `migrations migrate` | `cmd/migrations` | Прогон миграций БД |
|
||||||
|
| `delete_expired_public_links` | `cmd/scripts/delete_expired_public_links` | CronJob удаления просроченных публичных ссылок |
|
||||||
|
| `refresh_latest_bundle_filters_view` | `cmd/scripts/refresh_latest_bundle_filters_view` | CronJob обновления материализованного представления |
|
||||||
|
| `cleanup_failed_s32d_sessions` | `cmd/scripts/cleanup_failed_s32d_sessions` | CronJob очистки зависших s3d→ifc сессий |
|
||||||
|
|
||||||
|
Порядок запуска в контейнере: сначала миграции, затем сервер.
|
||||||
|
|
||||||
|
- `entrypoint.sh` (api): `migrations migrate` → `api`;
|
||||||
|
- `file_entrypoint.sh` (filestream): `migrations migrate` → `filestreamer`.
|
||||||
|
|
||||||
|
Для локальной разработки предусмотрен hot-reload через `air`: `.air.toml` (api, `./tmp/api`) и `.air.filestreamer.toml` (filestream, `./tmp/filestreamer`). Окружение сборки описано в `flake.nix` (Go 1.22 + `air`), образы собираются с Go 1.24 (`.docker/api.dockerfile`, `.docker/api-filestream.dockerfile`).
|
||||||
|
|
||||||
|
## HTTP-фреймворк, порты, health
|
||||||
|
|
||||||
|
- Роутер — `gorilla/mux`, обёрнутый в `rest.NewCustomRouter` (`gitlab.sarex.io/platform/gotools/rest`). Ответы оборачиваются в JSON (`rest.JSONResponse`), включена gzip-компрессия (`gorilla/handlers.CompressHandler`).
|
||||||
|
- Пробы `liveness`/`readiness` в Helm ходят на `GET /ping` (эндпоинт предоставляется кастомным роутером `rest`, в коде маршрутов репозитория не объявлен).
|
||||||
|
- Порт api — из `API_ADDRESS`, порт filestream — из `API_ADDRESS_FILE` (в k8s оба слушают `0.0.0.0:8080`).
|
||||||
|
- Таймауты: у api `Read/WriteTimeout = 30m` (жёстко в коде), у filestream `Read/WriteTimeout = READ_WRITE_TIMEOUT_FILE_STREAM` (по умолчанию окружения — `6h`).
|
||||||
|
- Оба сервиса регистрируют `net/http/pprof` (`/debug/pprof/...`).
|
||||||
|
|
||||||
|
## Аутентификация и авторизация
|
||||||
|
|
||||||
|
Разбор описан в `cmd/api/bootstrap.go`, `cmd/filestreamer/main.go`, `pkg/midleware/auth.go`, `pkg/midleware/signature.go`.
|
||||||
|
|
||||||
|
Цепочка middleware для `/api/v1/*`: `sentry` → `JSONResponse` → `reqid` → `logging` → (**только filestream**: `SignatureMiddleware`) → `auth.JWTToCtx` → `JWTUserExtractorFromCtx` → `DjangoToCtx` → `NewAuthMiddleware`/`NewAuthMiddlewareWithZitadel` → `DeleteJWTFromQueryMiddleware` → `sentry.AddUser`.
|
||||||
|
|
||||||
|
- **JWT**: токен из заголовка `Authorization: Bearer ...` проверяется по RSA-публичному ключу (`PUBLIC_KEY`, формат PEM/PKIX). Из claims извлекаются `company_ids` и `service_accounts`.
|
||||||
|
- **Zitadel** (опционально, `USE_ZITADEL=1`): дополнительная проверка токена через Zitadel и разбор метаданных пользователя (`urn:zitadel:iam:user:metadata`, base64-поля `company_ids`/`service_accounts`).
|
||||||
|
- **Identity-заголовок**: при наличии `Identity` метаданные берутся из него.
|
||||||
|
- **Публичные ссылки/временные загрузки**: пути `/api/v1/public/...`, `/api/v1/public_link_mrpas/...` и запросы с `download_type=temporary` проверяются по HMAC-секрету `DOCUMENT_PUBLIC_LINK_JWT_SECRET`.
|
||||||
|
- **Подписанные ссылки (только filestream)**: при наличии query-параметра `signature` `SignatureMiddleware` убирает `Authorization` и проверяет подпись (`SIGNATURE_SECRET_KEY`) с `expires_at`; включается флагом `ENABLE_SIGNATURE_IN_URL` (при `true` обязателен рабочий Valkey — иначе api/filestream завершается с кодом 2).
|
||||||
|
- Маршруты `/internal/v1/*` используют облегчённую цепочку (`auth.JWTToCtxIfPossible`) без обязательной проверки.
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
В столбце «Переменная» — точное имя (тег `envconfig`). Дефолт `—` означает, что тег `default` не задан (поле получает нулевое значение типа, если переменная не задана в окружении).
|
||||||
|
|
||||||
|
### PostgreSQL
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS` | string | — | Хост PostgreSQL |
|
||||||
|
| `POSTGRES_PORT` | string | — | Порт PostgreSQL |
|
||||||
|
| `POSTGRES_USER` | string | — | Пользователь БД |
|
||||||
|
| `POSTGRES_PASSWORD` | string | — | Пароль БД |
|
||||||
|
| `POSTGRES_DB` | string | — | Имя базы данных |
|
||||||
|
| `POSTGRES_POOL_SIZE` | int | — | Размер пула соединений (**только api**; filestreamer использует фиксированное значение 50) |
|
||||||
|
| `ENABLE_SSL` | bool | — | TLS-подключение к БД; при `true` используется `YC-PG-CERTIFICATE` |
|
||||||
|
| `YC-PG-CERTIFICATE` | string | — | PEM CA-сертификат PostgreSQL (имя с дефисами; в проде — из секрета `yc-pg-certificate`) |
|
||||||
|
|
||||||
|
### S3
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_S3` | bool | — | Включить S3. В api при `false` сервис стартует без S3-клиента; в filestream S3 нужен всегда |
|
||||||
|
| `S3_SERVICE_ACCOUNT` | string | — | Путь к JSON сервис-аккаунта S3 (монтируется как файл) |
|
||||||
|
| `S3_SERVICE_ACCOUNT_STR` | string | — | Альтернатива: JSON сервис-аккаунта строкой |
|
||||||
|
|
||||||
|
### API-адреса
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `API_ADDRESS` | string | — | Адрес прослушивания основного API (`cmd/api`) |
|
||||||
|
| `API_ADDRESS_FILE` | string | — | Адрес прослушивания файлового стримера (`cmd/filestreamer`) |
|
||||||
|
|
||||||
|
### Sarex backend (Django) и Zitadel
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DJANGO_HOST` | string | — | Базовый URL Sarex backend (Django). Используется клиентами `django`, `users`, `sarex_backend`, `accounts` |
|
||||||
|
| `DJANGO_BASIC_AUTH` | string | — | Basic-auth для системных вызовов Django |
|
||||||
|
| `DJANGO_BASIC_AUTH_FOR_GET_USER` | string | — | Отдельный basic-auth для запросов пользователей |
|
||||||
|
| `DJANGO_ORIGINATOR` | string | — | Идентификатор источника запросов |
|
||||||
|
| `USE_ZITADEL` | bool | — | Включить проверку токенов через Zitadel |
|
||||||
|
| `ZITADEL_DOMAIN` | string | — | Домен Zitadel (IdP) |
|
||||||
|
| `ZITADEL_ACCOUNT` | string | — | Путь к JSON сервис-аккаунта Zitadel |
|
||||||
|
|
||||||
|
### Внешние сервисы (базовые URL)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `FILE_URL_EXTERNAL` | string | — | Внешний URL файлового сервиса |
|
||||||
|
| `DOCUMENTATION_URL` | string | — | URL самого сервиса документаций (для внутренних ссылок) |
|
||||||
|
| `WORKFLOW_URL` | string | — | URL сервиса workflows (создание/чтение процессов обработки) |
|
||||||
|
| `WORKSPACE_URL` | string | — | URL сервиса воркспейсов |
|
||||||
|
| `WORKSPACE_V2_EXTERNAL_URL` | string | — | Внешний URL воркспейсов v2 |
|
||||||
|
| `WORKSPACE_BUNDLE_VERSION` | string | — | Версия бандла воркспейса (`v1`) |
|
||||||
|
| `MARKS_PROCESSING_URL` | string | — | URL сервиса штампов/маркировок (при HTTP-режиме) |
|
||||||
|
| `BIM_API_URL` | string | — | URL BIM-API v1 |
|
||||||
|
| `BIM_API_V2_URL` | string | — | URL BIM-API v2 (bim-core-api) |
|
||||||
|
| `BIM_API_URL_EXTERNAL` | string | — | Внешний URL BIM-API |
|
||||||
|
| `SYSTEM_LOG_URL` | string | — | URL сервиса системного лога |
|
||||||
|
| `FLOWS_URL` | string | — | URL сервиса flows |
|
||||||
|
| `AUTOMATION_URL` | string | — | URL сервиса автоматизаций |
|
||||||
|
| `TRANSMITTALS_BASE_URL` | string (nullable) | `nil` | URL сервиса трансмитталов; клиент создаётся только если переменная задана |
|
||||||
|
|
||||||
|
### Публичные ссылки и JWT
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `PUBLIC_LINK_HOST` | string | — | Хост публичных ссылок на документы |
|
||||||
|
| `PUBLIC_KEY` | string | — | RSA-публичный ключ (PEM/PKIX) для проверки JWT |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_SECRET` | string | — | HMAC-секрет для JWT публичных ссылок и временных загрузок |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_EXPIRATION_MINUTES` | uint8 | — | Время жизни JWT публичной ссылки (мин.) |
|
||||||
|
| `PUBLIC_LINK_FOLDER_CONNECTOR_ENABLED` | bool | `true` | Коннектор публичных ссылок для папок |
|
||||||
|
|
||||||
|
### Подпись ссылок (signature-in-URL)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_SIGNATURE_IN_URL` | bool | `false` | Проверять подпись в URL. При `true` требуется рабочий Valkey (иначе сервис завершается с кодом 2) |
|
||||||
|
| `SIGNATURE_SECRET_KEY` | string | `""` | Секрет для подписи ссылок скачивания |
|
||||||
|
| `SIGNATURE_IN_URL_EXPIRATION_SECONDS` | uint64 | `600` | Срок жизни подписи (сек.) |
|
||||||
|
| `ENABLE_AUTH_JWT_IN_URL` | bool | `true` | Добавлять `auth_jwt` в URL |
|
||||||
|
|
||||||
|
### Valkey (Redis-совместимый) — кэши
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `VALKEY_ADDR` | string | `localhost:6380` | Адрес `host:port`. Пустая строка полностью отключает клиент Valkey |
|
||||||
|
| `VALKEY_LOGIN` | string | `""` | Логин |
|
||||||
|
| `VALKEY_HOST` | string | `""` | Хост (доп. поле) |
|
||||||
|
| `VALKEY_PASSWORD` | string | `""` | Пароль |
|
||||||
|
| `VALKEY_DB` | int | `0` | Номер БД Redis/Valkey |
|
||||||
|
| `VALKEY_CACHE_TTL` | duration | `1h` | TTL кэша |
|
||||||
|
| `VALKEY_SSL` | bool | `false` | TLS-подключение |
|
||||||
|
| `VALKEY_SSL_CA_CERTS` | string | `""` | CA-сертификат для TLS |
|
||||||
|
|
||||||
|
### Файловый стример и in-memory кэш
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `READ_WRITE_TIMEOUT_FILE_STREAM` | duration | — | Read/Write-таймаут HTTP-сервера filestream (в окружении — `6h`) |
|
||||||
|
| `USE_CACHE_IN_FILE_STREAMER` | bool | — | Включить in-memory кэш в filestream-хранилище |
|
||||||
|
| `CACHE_DEFAULT_EXPIRATION` | duration | — | TTL записей кэша |
|
||||||
|
| `CACHE_CLEANUP_INTERVAL` | duration | — | Интервал очистки кэша |
|
||||||
|
|
||||||
|
### BIM / обработка файлов
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USE_BIMV1_FOR_BIMV2` | bool | — | Использовать BIM v1 для v2 |
|
||||||
|
| `USE_BIM_INSERTER` | bool | `true` | Включить BIM-inserter |
|
||||||
|
| `USE_LEGACY_BIM_FLOW` | bool | `false` | Старый flow BIM |
|
||||||
|
| `LAST_MASTER_BIM` | uint64 | — | Граница master-BIM |
|
||||||
|
| `LAST_SLAVE_1_BIM` | uint64 | — | Граница slave-1-BIM |
|
||||||
|
| `LAST_SLAVE_2_BIM` | uint64 | — | Граница slave-2-BIM |
|
||||||
|
| `CONVERT_DWG_TO_GEOJSON` | bool | `true` | Конвертация DWG→GeoJSON |
|
||||||
|
| `CONVERT_DXF_TO_GEOJSON` | bool | `true` | Конвертация DXF→GeoJSON |
|
||||||
|
| `IS_CONVERTED_PDF_UPLOADING_TO_S3` | bool | `true` | Загружать сконвертированный PDF в S3 |
|
||||||
|
| `DELETE_S3D_AFTER_MESHOPT` | bool | `false` | Удалять s3d после mesh-оптимизации |
|
||||||
|
| `WORKFLOW_IMAGES_VERSION` | string | — | Тег образов workflow |
|
||||||
|
| `WORKFLOWS_IMAGES_VERSION` | string | — | Тег образов задач обработки (используется клиентом `workflow`) |
|
||||||
|
| `CONTAINER_REGISTRY` | string | `cr.yandex/crp3ccidau046kdj8g9q` | Реестр контейнеров для образов задач |
|
||||||
|
| `WORKFLOWS_CONFIG_FILEPATH` | string | `.example.tasks_execution_config.json` | Путь к JSON с ресурсами задач обработки |
|
||||||
|
|
||||||
|
### Штампы/маркировки (HTTP или RabbitMQ)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USE_MARKS_RABBITMQ` | bool | `0` | `0` — ходить в `MARKS_PROCESSING_URL` по HTTP; `1` — через RabbitMQ |
|
||||||
|
| `MARKS_RABBITMQ_HOST` | string | `""` | Хост RabbitMQ |
|
||||||
|
| `MARKS_RABBITMQ_PORT` | string | `""` | Порт RabbitMQ |
|
||||||
|
| `MARKS_RABBITMQ_USER` | string | `""` | Пользователь (из секрета) |
|
||||||
|
| `MARKS_RABBITMQ_PASSWORD` | string | `""` | Пароль (из секрета) |
|
||||||
|
| `MARKS_RABBITMQ_API` | string | `""` | Vhost/имя очереди |
|
||||||
|
|
||||||
|
### Rate limit эндпоинта метаданных
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `METADATA_RATE_LIMIT_ENABLED` | bool | `true` | Включить rate-limit для `/documents/metadata` |
|
||||||
|
| `METADATA_RATE_LIMIT_MAX_REQUESTS` | int | `10` | Макс. число запросов |
|
||||||
|
|
||||||
|
### Почта
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_MAILGUN` | bool | `true` | Флаг использования Mailgun |
|
||||||
|
| `ENABLE_SMTP` | bool | `false` | Флаг использования SMTP |
|
||||||
|
|
||||||
|
### Наблюдаемость
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENVIRONMENT` | string | — | Окружение (для Sentry/трейсинга) |
|
||||||
|
| `SENTRY_DSN` | string | — | DSN Sentry |
|
||||||
|
| `SENTRY_DEBUG` | bool | — | Отладка Sentry |
|
||||||
|
| `NAMESPACE` | string | — | Неймспейс (для контекста запусков задач) |
|
||||||
|
| `ENABLE_SQL_QUERY` | bool | — | Логировать SQL-запросы |
|
||||||
|
| `TRACER_USE` | bool | `false` | Включить OpenTelemetry-трейсинг |
|
||||||
|
| `TRACER_HOST` | string | `localhost:4317` | Адрес OTLP-коллектора |
|
||||||
|
| `TRACER_USE_INSECURE` | bool | `true` | Подключение к коллектору без TLS |
|
||||||
|
| `TRACER_LOGGER_NAME` | string | `tracer_logger` | Имя логгера трейсинга |
|
||||||
|
| `SERVICE_NAME` | string | `documentations-api` | Имя сервиса в трейсах (используется api) |
|
||||||
|
| `SERVICE_NAME_FILESTREAM` | string | `filestream-api` | Имя сервиса в трейсах (используется filestream) |
|
||||||
|
|
||||||
|
## Различия api и filestream
|
||||||
|
|
||||||
|
Оба процесса читают одну и ту же структуру `config.Config`, но:
|
||||||
|
|
||||||
|
| Аспект | api (`cmd/api`) | filestream (`cmd/filestreamer`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Слушает адрес из | `API_ADDRESS` | `API_ADDRESS_FILE` |
|
||||||
|
| Пул соединений к БД | `POSTGRES_POOL_SIZE` | фиксировано `50` (+ `PoolTimeout=1m`), `POSTGRES_POOL_SIZE` игнорируется |
|
||||||
|
| Read/Write-таймаут сервера | жёстко `30m` | `READ_WRITE_TIMEOUT_FILE_STREAM` |
|
||||||
|
| S3 | опционален (`ENABLE_S3`) | обязателен (при отсутствии кредов процесс завершается) |
|
||||||
|
| In-memory кэш хранилища | выключен | управляется `USE_CACHE_IN_FILE_STREAMER` / `CACHE_*` |
|
||||||
|
| Имя сервиса в трейсах | `SERVICE_NAME` | `SERVICE_NAME_FILESTREAM` |
|
||||||
|
| Signature-middleware на `/api/v1` | нет | есть (`SignatureMiddleware`) |
|
||||||
|
| Набор маршрутов | полный REST CRUD (`cmd/api/routes_api.go`, `routes_internal.go`) | только потоковые скачивания/загрузки файлов (`cmd/filestreamer/routes_api.go`, `routes_internal.go`) |
|
||||||
|
|
||||||
|
**Что делает filestream-бинарник.** Это отдельный HTTP-сервис для тяжёлой потоковой работы с файлами, вынесенный из основного API, чтобы не блокировать его долгими соединениями (отсюда таймаут в часы и увеличенный пул БД). Публичные маршруты (`/api/v1`):
|
||||||
|
|
||||||
|
- `GET /documents/folders` — скачивание нескольких папок архивом (gzip);
|
||||||
|
- `GET|HEAD|POST /bundles/...` — скачивание файлов бандла; при `?format=gz` отдаётся без повторного сжатия, иначе — `CompressHandler`; поддержаны HEAD (range) и внешний матчер `DownloadMatcherExternal`;
|
||||||
|
- `GET|HEAD|POST /pages/...` — скачивание страниц (постранично);
|
||||||
|
- `GET|HEAD /documents/...` — скачивание по документам; `POST /documents/...` — скачивание по списку bundle-id;
|
||||||
|
- `POST /bundles_mrpas/...` и `GET /public_link_mrpas/...` — выгрузка MRPA (в т. ч. по публичной ссылке).
|
||||||
|
|
||||||
|
Внутренние маршруты (`/internal/v1`): скачивание/загрузка бандлов между сервисами и `POST /upload_finish/bundles/...`.
|
||||||
|
|
||||||
|
## Переменные сборки и запуска (не читаются кодом приложения)
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `APP_VERSION` | `Makefile`, dockerfiles | Версия, зашиваемая в бинарь (`-ldflags -X main.version`) |
|
||||||
|
| `CI_COMMIT_SHORT_SHA` | dockerfiles | Тег версии образа files |
|
||||||
|
| `GITLAB_CREDENTIALS` | dockerfiles (build-arg) | Доступ к приватным Go-модулям `gitlab.sarex.io` |
|
||||||
|
| `API_VERSION` | `.docker/docker-compose.yml` | Тег локально запускаемого образа |
|
||||||
|
| `POSTGRES_EXTERNAL_PORT` | `.docker/docker-compose.yml` | Внешний порт локального Postgres |
|
||||||
|
| `KEY_JWT` / `JWT_KEY` | `.docker/.env`, docker-compose | JWT-ключ **только для интеграционных тестов**, кодом приложения не читается |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Чарт основан на `universal-chart` (зависимость из `Chart.yaml`) и описывает два сервиса — `services.api` и `services.filestream`. У каждого свои блоки `envs` (обычные значения, с разбивкой по окружениям `_default`/`stage`/`preprod`/`production`) и `secretEnvs` (значения из k8s-секретов). Наборы переменных у обоих сервисов практически идентичны.
|
||||||
|
|
||||||
|
Значения из секретов (`secretEnvs`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secretName`) | Ключ (`secretKey`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SIGNATURE_SECRET_KEY` | `documentations-download-secret` | `secret` |
|
||||||
|
| `VALKEY_ADDR` | `valkey-secret` | `url` |
|
||||||
|
| `VALKEY_LOGIN` | `valkey-secret` | `login` |
|
||||||
|
| `VALKEY_PASSWORD` | `valkey-secret` | `password` |
|
||||||
|
| `VALKEY_HOST` | `valkey-secret` | `host` |
|
||||||
|
| `VALKEY_PORT` | `valkey-secret` | `port` |
|
||||||
|
| `VALKEY_CA_CERTS` | `valkey-secret` | `cert` |
|
||||||
|
| `POSTGRES_USER` | `documentations-postgresql-secret` | `user` |
|
||||||
|
| `POSTGRES_PORT` | `documentations-postgresql-secret` | `port` |
|
||||||
|
| `POSTGRES_ADDRESS` | `documentations-postgresql-secret` | `host` |
|
||||||
|
| `POSTGRES_DB` | `documentations-postgresql-secret` | `database` |
|
||||||
|
| `POSTGRES_PASSWORD` | `documentations-postgresql-secret` | `password` |
|
||||||
|
| `DJANGO_BASIC_AUTH` | `django-auth` | `key` |
|
||||||
|
| `DJANGO_BASIC_AUTH_FOR_GET_USER` | `django-auth-get-user` | `key` |
|
||||||
|
| `DOCUMENT_PUBLIC_LINK_JWT_SECRET` | `yc-jwt-secret` | `secret` |
|
||||||
|
| `PUBLIC_KEY` | `public-key` | `key` |
|
||||||
|
| `MARKS_RABBITMQ_USER` | `cde-rabbitmq-secret` (в api; prod — `marks-rabbit-secret`) / `marks-rabbit-secret` (в filestream) | `user` |
|
||||||
|
| `MARKS_RABBITMQ_PASSWORD` | то же | `password` |
|
||||||
|
| `YC-PG-CERTIFICATE` | `yc-pg-certificate` | `certificate` |
|
||||||
|
|
||||||
|
Тома (`volumes`) монтируют секреты как файлы: `documentations-yc-s3` → `/etc/sarex/yc-s3-storage` (на него указывает `S3_SERVICE_ACCOUNT`), `zitadel-account` → `/etc/sarex/zitadel` (на него указывает `ZITADEL_ACCOUNT`). Файл `WORKFLOWS_CONFIG_FILEPATH` в проде — `/etc/app/tasks_execution_config.json`.
|
||||||
|
|
||||||
|
Ingress включён только у `filestream` (`ingress.enabled: false` по умолчанию, path `/files/api/` → rewrite `/api/`); у api ingress-блока в values нет — сервис доступен через `documentations-api-svc`.
|
||||||
|
|
||||||
|
Прочие значения чарта (не переменные приложения): `deployment.*` (реплики stage/preprod/prod = 1/3/6, ресурсы, revisionHistoryLimit), `probes` (`/ping`), `service.*`, `serviceAccount`, `imagePullSecrets: dockerhub`, `affinity` (podAntiAffinity у filestream), а также блок `cronjobs` (`delete_expired_public_links`, `refresh_latest_bundle_filters_view`, `refresh_string_path_materialized_view`).
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`). Окружение переключается по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | NAMESPACE | universal-chart env |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `documentations` | `stage` |
|
||||||
|
| ветка `master` | `preprod` | `documentations-preprod` | `preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `production` |
|
||||||
|
| merge request | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||||||
|
|
||||||
|
Общие для всех окружений: `RELEASE_NAME=documentations`, `CHART_NAME=documentations`, `SERVICE_NAME: documentations`, `DOCKERFILE_PATH: .docker/api.dockerfile`, `IMAGE_PATH: api.deployment.image`. Через `HELM_SET_ARGS` проставляются образы: `services.api.image` (основной), `services.filestream.image` (`IMAGE_NAME_API_FILES`, dockerfile `.docker/api-filestream.dockerfile`), а также образы кронджоб `cronjobs.delete_expired_public_links`, `cronjobs.refresh_latest_bundle_filters_view`, `cronjobs.cleanup_failed_s32d_sessions`. Дополнительные образы собираются отдельными job-ами `build_files`, `build_public_link_autodeletion`, `build_refresh_latest_bundle_filters_view`, `build_cleanup_failed_s32d_sessions` (только на `stage`/`master`/тег).
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **У envconfig нет «обязательных» полей.** Отсутствующая переменная без `default` становится нулевым значением, ошибка старта не выбрасывается. Некорректная конфигурация проявляется в рантайме (не удаётся подключиться к БД/S3, невалидный `PUBLIC_KEY` при первой проверке JWT и т. п.).
|
||||||
|
- **`.env` не подхватывается автоматически** — приложение читает только окружение процесса. Локально удобнее запускать через `docker-compose --env-file` (`make docker`) или экспортировать `.docker/.env` вручную.
|
||||||
|
- **`POSTGRES_POOL_SIZE` игнорируется в filestream** — там пул жёстко задан как `50`. В api берётся из переменной.
|
||||||
|
- **`YC-PG-CERTIFICATE`** — имя с дефисами (не в стиле `SNAKE_CASE`), но envconfig читает его по точному тегу. Значение приходит из секрета и используется как содержимое PEM (`AppendCertsFromPEM`), а не как путь к файлу.
|
||||||
|
- **Рассинхрон имён Valkey.** В Helm задаются `VALKEY_PORT` и `VALKEY_CA_CERTS`, но код читает `VALKEY_ADDR` (host:port одной строкой) и `VALKEY_SSL_CA_CERTS`. Переменные `VALKEY_PORT`/`VALKEY_CA_CERTS` приложением напрямую не читаются (адрес и CA берутся из `VALKEY_ADDR`/`VALKEY_SSL_CA_CERTS`).
|
||||||
|
- **`HOST` и `DOCUMENTATION_EXTERNAL_URL` из Helm кодом не читаются** — в `config.Config` таких полей нет (для внутренних ссылок используется `DOCUMENTATION_URL`, `FILE_URL_EXTERNAL`, `PUBLIC_LINK_HOST`).
|
||||||
|
- **`ENABLE_SIGNATURE_IN_URL=true` требует Valkey.** Если клиент Valkey не инициализировался, а флаг включён, оба процесса завершаются с кодом `2`.
|
||||||
|
- **Флаги `ENABLE_MAILGUN`/`ENABLE_SMTP`** присутствуют в конфиге и Helm, но собственной отправкой почты сервис не занимается (в отличие от transmittal-api); это флаги для внешних интеграций.
|
||||||
|
- **`TRANSMITTALS_BASE_URL` — nullable.** Клиент трансмитталов создаётся только если переменная задана; иначе связанные вызовы пропускаются.
|
||||||
|
- **Штампы: HTTP vs RabbitMQ.** При `USE_MARKS_RABBITMQ=1` используется RPC-клиент через RabbitMQ (`MARKS_RABBITMQ_*`), при `0` — HTTP-клиент на `MARKS_PROCESSING_URL`. В values для prod-секретов имя `marks-rabbit-secret` отличается от stage/preprod (`cde-rabbitmq-secret`).
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Ориентир — `.docker/.env` (+ `make docker` для запуска в контейнерах вместе с Postgres). Минимально нужно задать:
|
||||||
|
|
||||||
|
- `API_ADDRESS`, `API_ADDRESS_FILE`;
|
||||||
|
- `POSTGRES_ADDRESS`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`, `POSTGRES_POOL_SIZE`, `ENABLE_SSL=0`;
|
||||||
|
- `ENABLE_S3` (`0` для api без S3; для filestream — `1` и `S3_SERVICE_ACCOUNT`/`S3_SERVICE_ACCOUNT_STR`);
|
||||||
|
- `DJANGO_HOST`, `DJANGO_ORIGINATOR`, `NAMESPACE`;
|
||||||
|
- сервисные URL по необходимости: `WORKFLOW_URL`, `WORKSPACE_URL`, `SYSTEM_LOG_URL`, `FLOWS_URL`, `MARKS_PROCESSING_URL`;
|
||||||
|
- для filestream: `READ_WRITE_TIMEOUT_FILE_STREAM`, `USE_CACHE_IN_FILE_STREAMER`, `CACHE_DEFAULT_EXPIRATION`, `CACHE_CLEANUP_INTERVAL`;
|
||||||
|
- `ENABLE_SQL_QUERY=1` (для отладки), `TRACER_USE=false`;
|
||||||
|
- `PUBLIC_KEY`/`DOCUMENT_PUBLIC_LINK_JWT_SECRET` — для реальной проверки JWT (локально можно оставить пустыми, но защищённые ручки будут отклонять токены).
|
||||||
|
|
||||||
|
Готовый пример со всеми значениями приведён в `api.env.example` (рядом с этим документом) и в `.docker/.env` исходного репозитория.
|
||||||
114
apps/documentations/api.ENDPOINTS.md
Normal file
114
apps/documentations/api.ENDPOINTS.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Эндпоинты внешних сервисов, с которыми взаимодействует documentations-api
|
||||||
|
|
||||||
|
Документ описывает HTTP-эндпоинты внешних сервисов, к которым обращается сервис документаций (оба бинарника — `cmd/api` и `cmd/filestreamer`). В отличие от фронтенда, единого декларативного реестра эндпоинтов здесь нет — каждый внешний сервис инкапсулирован в собственном клиенте в каталогах `clients/` и `pkg/`.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Клиенты создаются при старте (`cmd/api/routes_api.go`, `cmd/filestreamer/*`) и используют базовый URL из соответствующей переменной окружения (см. `config/config.go`). Большинство клиентов построены на `go-resty/resty` (метод `SetHostURL`/`SetBaseURL`), часть — на внутренних http-обёртках `gitlab.sarex.io/platform/gotools`. Итоговый URL = `<базовый URL сервиса>` + путь из клиента.
|
||||||
|
|
||||||
|
Аутентификация исходящих запросов:
|
||||||
|
|
||||||
|
- к Sarex backend (Django) — HTTP Basic (`DJANGO_BASIC_AUTH`, а для получения пользователей — `DJANGO_BASIC_AUTH_FOR_GET_USER`); для части ручек проксируется заголовок `Identity`/`Bearer`;
|
||||||
|
- к остальным сервисам — по внутренней сети кластера, как правило без внешней авторизации.
|
||||||
|
|
||||||
|
## Базовые URL по сервисам
|
||||||
|
|
||||||
|
| Сервис | Переменная окружения | Клиент (каталог) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Sarex backend (Django) | `DJANGO_HOST` | `clients/django`, `pkg/django`, `pkg/users`, `pkg/sarex_backend`, `clients/accounts` |
|
||||||
|
| Flows | `FLOWS_URL` | `pkg/flows` |
|
||||||
|
| Workflows | `WORKFLOW_URL` | `clients/workflow`, `pkg/workflows` |
|
||||||
|
| Workspaces | `WORKSPACE_URL` | `clients/workspace` |
|
||||||
|
| Transmittals | `TRANSMITTALS_BASE_URL` (опц.) | `pkg/transmittal` |
|
||||||
|
| Automation | `AUTOMATION_URL` | `pkg/automation` |
|
||||||
|
| Marks (штампы, HTTP) | `MARKS_PROCESSING_URL` | `pkg/marks/base` |
|
||||||
|
| Marks (штампы, RabbitMQ) | `MARKS_RABBITMQ_*` | `pkg/marks/rpc` |
|
||||||
|
| BIM-API v1 | `BIM_API_URL` | `clients/bim-api` |
|
||||||
|
| BIM-API v2 (bim-core-api) | `BIM_API_V2_URL` | `clients/bim-api-v2` |
|
||||||
|
| System log | `SYSTEM_LOG_URL` | `pkg/system_log` |
|
||||||
|
|
||||||
|
Дополнительно сервис работает с S3 (объектное хранилище, креды из `S3_SERVICE_ACCOUNT`/`S3_SERVICE_ACCOUNT_STR`) и PostgreSQL — это не HTTP-сервисы и в таблицах ниже не приводятся.
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### Sarex backend (Django) — `DJANGO_HOST`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/core/users/` | `clients/django/users.go` | Список пользователей |
|
||||||
|
| GET | `/api/core/users/{id}/` | `clients/django/users.go`, `pkg/users/client.go` | Пользователь по id |
|
||||||
|
| GET | `/api/core/users/{id}/introspect` | `clients/django/users.go` | Интроспекция пользователя |
|
||||||
|
| GET | `/api/core/users/{id}` | `pkg/django/client.go` | Пользователь по id (внутренний клиент) |
|
||||||
|
| GET | `/api/client/settings/` | `clients/django/settings.go` | Клиентские настройки |
|
||||||
|
| GET | `/api/core/service-accounts/personalized/` | `clients/django/settings.go` | Персонализированные сервисные аккаунты |
|
||||||
|
| GET | `/api/core/service_accounts/` | `clients/django/service_accounts.go`, `clients/accounts` | Сервисные аккаунты |
|
||||||
|
| GET | `/api/core/companies/` | `clients/django/companies.go` | Список компаний |
|
||||||
|
| GET | `/api/core/mrpa/{id}/` | `pkg/sarex_backend/client.go` | MRPA по id (прокидывается заголовок `Identity`) |
|
||||||
|
|
||||||
|
### Flows — `FLOWS_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `internal/v1/documents/?full=true&document_ids={id}` | `pkg/flows/client.go` | Документы в процессах (flows) по id |
|
||||||
|
| GET/POST | `internal/v1/documents/` | `pkg/flows/client.go` | Документы процессов |
|
||||||
|
|
||||||
|
### Workflows — `WORKFLOW_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `internal/v1/companies/{company_id}/workflows` | `clients/workflow/client.go` | Создать workflow обработки (BIM/PDF/DWG/DEM/DOCX и т. д.); образы задач — из `CONTAINER_REGISTRY` + `WORKFLOWS_IMAGES_VERSION` |
|
||||||
|
| GET | `v1/workflows/{id}` | `pkg/workflows/client.go` | Прочитать workflow по id |
|
||||||
|
|
||||||
|
### Workspaces — `WORKSPACE_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `internal/v2/workspaces` | `clients/workspace/client.go` | Создать воркспейс |
|
||||||
|
| DELETE | `internal/v2/documents/{ids}` | `clients/workspace/client.go` | Удалить документы воркспейса |
|
||||||
|
| PATCH | `internal/v2/documents/restore` | `clients/workspace/client.go` | Восстановить документы воркспейса |
|
||||||
|
|
||||||
|
### Transmittals — `TRANSMITTALS_BASE_URL`
|
||||||
|
|
||||||
|
Клиент создаётся только если переменная задана.
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/internal/v1/transmittals/by_bundle_ids` | `pkg/transmittal/client.go` | Трансмитталы по списку bundle-id |
|
||||||
|
|
||||||
|
### Automation — `AUTOMATION_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| — | `/internal/v1/automations/{process_name}` | `pkg/automation/client.go` | Запуск/получение автоматизации по имени процесса |
|
||||||
|
|
||||||
|
### Marks (штампы/маркировки)
|
||||||
|
|
||||||
|
Режим выбирается флагом `USE_MARKS_RABBITMQ`.
|
||||||
|
|
||||||
|
| Метод | Путь / транспорт | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/api/v1/marks/{bundle_id}` (HTTP, `MARKS_PROCESSING_URL`) | `pkg/marks/base/client.go` | Наложение штампов на бандл (HTTP-режим) |
|
||||||
|
| — | RabbitMQ (`MARKS_RABBITMQ_*`) | `pkg/marks/rpc` | Наложение штампов через очередь (RPC-режим, не HTTP) |
|
||||||
|
|
||||||
|
### BIM-API v1 — `BIM_API_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/internal/v1/targets/{target_id}/bims-pdm` | `clients/bim-api/client.go` | Создать BIM для target (PDM) |
|
||||||
|
| POST | `/internal/v1/targets/{target_id}/bims-v2-pdm` | `clients/bim-api/client.go` | Создать BIM v2 для target (PDM) |
|
||||||
|
|
||||||
|
### BIM-API v2 (bim-core-api) — `BIM_API_V2_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/internal/v1/projects/{project_id}/bims` | `clients/bim-api-v2/client.go` | Создать BIM для проекта |
|
||||||
|
|
||||||
|
### System log — `SYSTEM_LOG_URL`
|
||||||
|
|
||||||
|
| Метод | Путь | Клиент | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/api/v0/system_log` | `pkg/system_log/client.go` | Отправить запись в системный лог |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Клиенты, как правило, проверяют код ответа и оборачивают ошибку через `github.com/rotisserie/eris` (напр. «invalid response code %d expected 200»). Для случая недоступности исходного сервиса в самом API определён нестандартный статус `523` (`network/consts.go`, `StatusOriginIsUnreachable`).
|
||||||
131
apps/documentations/api.env.example
Normal file
131
apps/documentations/api.env.example
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# documentations-api / documentations-filestream — пример переменных окружения
|
||||||
|
# =============================================================================
|
||||||
|
# Все переменные читаются напрямую из окружения процесса библиотекой
|
||||||
|
# kelseyhightower/envconfig (config/config.go). Префикса и вложенности НЕТ —
|
||||||
|
# имена плоские. Значения ниже — ориентир для локального запуска (аналог
|
||||||
|
# .docker/.env). Оба бинарника (api и filestreamer) используют один и тот же
|
||||||
|
# набор переменных.
|
||||||
|
|
||||||
|
# --- Адреса прослушивания ----------------------------------------------------
|
||||||
|
API_ADDRESS=0.0.0.0:6666 # порт основного API (cmd/api)
|
||||||
|
API_ADDRESS_FILE=0.0.0.0:7777 # порт файлового стримера (cmd/filestreamer)
|
||||||
|
|
||||||
|
# --- PostgreSQL --------------------------------------------------------------
|
||||||
|
POSTGRES_ADDRESS=127.0.0.1
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_USER=user
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
POSTGRES_DB=documentations
|
||||||
|
POSTGRES_POOL_SIZE=10 # учитывается только в api; filestreamer жёстко использует 50
|
||||||
|
ENABLE_SSL=0 # 1 — TLS к БД, тогда нужен YC-PG-CERTIFICATE
|
||||||
|
# YC-PG-CERTIFICATE= # PEM CA-сертификат PostgreSQL (в проде — из секрета)
|
||||||
|
|
||||||
|
# --- S3 (объектное хранилище) ------------------------------------------------
|
||||||
|
ENABLE_S3=0 # в api при 0 сервис стартует без S3; в filestreamer S3 обязателен
|
||||||
|
S3_SERVICE_ACCOUNT=/etc/sarex/yc_s3_doc_account.json # путь к JSON сервис-аккаунта
|
||||||
|
# S3_SERVICE_ACCOUNT_STR= # альтернатива: сам JSON строкой
|
||||||
|
|
||||||
|
# --- Sarex backend (Django) --------------------------------------------------
|
||||||
|
DJANGO_HOST=https://stage.sarex.io
|
||||||
|
DJANGO_BASIC_AUTH= # basic-auth для системных вызовов Django
|
||||||
|
DJANGO_BASIC_AUTH_FOR_GET_USER= # отдельный basic-auth для получения пользователей
|
||||||
|
DJANGO_ORIGINATOR=docs_local
|
||||||
|
|
||||||
|
# --- Zitadel (опциональная аутентификация) -----------------------------------
|
||||||
|
USE_ZITADEL=0
|
||||||
|
ZITADEL_DOMAIN=idp.dev.stage.sarex.io
|
||||||
|
ZITADEL_ACCOUNT=/etc/sarex/zitadel/zitadel-account.json
|
||||||
|
|
||||||
|
# --- Внешние сервисы (базовые URL) -------------------------------------------
|
||||||
|
FILE_URL_EXTERNAL=https://stage-api.sarex.io/files
|
||||||
|
DOCUMENTATION_URL=http://localhost:8000/
|
||||||
|
WORKFLOW_URL=https://stage-api.sarex.io/workflows/api
|
||||||
|
WORKSPACE_URL=https://stage-api.sarex.io/worspace/api
|
||||||
|
WORKSPACE_V2_EXTERNAL_URL=https://stage.sarex.io/workspaces-v2/
|
||||||
|
WORKSPACE_BUNDLE_VERSION=v1
|
||||||
|
MARKS_PROCESSING_URL=http://marks-service.documentations:8000
|
||||||
|
BIM_API_URL=http://bim-api-service.bim-api-stage/
|
||||||
|
BIM_API_V2_URL=http://bim-core-api.platform.svc.cluster.local:8000/
|
||||||
|
BIM_API_URL_EXTERNAL=https://stage-api.sarex.io/bim
|
||||||
|
SYSTEM_LOG_URL=http://localhost:8888
|
||||||
|
FLOWS_URL=http://backend-service.proc.svc.cluster.local:8000
|
||||||
|
AUTOMATION_URL=http://automation-api-service.automation-stage.svc.cluster.local:8000
|
||||||
|
# TRANSMITTALS_BASE_URL=http://transmittal-service.documentations:8000 # опционально
|
||||||
|
|
||||||
|
# --- Публичные ссылки на документы -------------------------------------------
|
||||||
|
PUBLIC_LINK_HOST=https://document-link.stage.sarex.io
|
||||||
|
PUBLIC_KEY= # PEM RSA public key (PKIX) для проверки JWT
|
||||||
|
DOCUMENT_PUBLIC_LINK_JWT_SECRET= # секрет HMAC для JWT публичных ссылок/временных загрузок
|
||||||
|
DOCUMENT_PUBLIC_LINK_JWT_EXPIRATION_MINUTES=5
|
||||||
|
PUBLIC_LINK_FOLDER_CONNECTOR_ENABLED=1
|
||||||
|
|
||||||
|
# --- Подпись ссылок скачивания (signature-in-URL) ----------------------------
|
||||||
|
ENABLE_SIGNATURE_IN_URL=false # при true обязателен рабочий Valkey и SIGNATURE_SECRET_KEY
|
||||||
|
SIGNATURE_SECRET_KEY=
|
||||||
|
SIGNATURE_IN_URL_EXPIRATION_SECONDS=600
|
||||||
|
ENABLE_AUTH_JWT_IN_URL=true
|
||||||
|
|
||||||
|
# --- Valkey / Redis (кэши, кэш подписей) -------------------------------------
|
||||||
|
VALKEY_ADDR=localhost:6380 # host:port; пустая строка полностью отключает клиент
|
||||||
|
VALKEY_LOGIN=
|
||||||
|
VALKEY_PASSWORD=
|
||||||
|
VALKEY_HOST=
|
||||||
|
VALKEY_DB=0
|
||||||
|
VALKEY_CACHE_TTL=1h
|
||||||
|
VALKEY_SSL=false
|
||||||
|
VALKEY_SSL_CA_CERTS=
|
||||||
|
|
||||||
|
# --- Файловый стример / кэш --------------------------------------------------
|
||||||
|
READ_WRITE_TIMEOUT_FILE_STREAM=6h
|
||||||
|
USE_CACHE_IN_FILE_STREAMER=true
|
||||||
|
CACHE_DEFAULT_EXPIRATION=30s
|
||||||
|
CACHE_CLEANUP_INTERVAL=35s
|
||||||
|
|
||||||
|
# --- BIM / обработка ---------------------------------------------------------
|
||||||
|
USE_BIMV1_FOR_BIMV2=1
|
||||||
|
USE_BIM_INSERTER=1
|
||||||
|
USE_LEGACY_BIM_FLOW=0
|
||||||
|
LAST_MASTER_BIM=1541
|
||||||
|
LAST_SLAVE_1_BIM=5000
|
||||||
|
LAST_SLAVE_2_BIM=15000
|
||||||
|
CONVERT_DWG_TO_GEOJSON=true
|
||||||
|
CONVERT_DXF_TO_GEOJSON=true
|
||||||
|
IS_CONVERTED_PDF_UPLOADING_TO_S3=true
|
||||||
|
DELETE_S3D_AFTER_MESHOPT=false
|
||||||
|
WORKFLOW_IMAGES_VERSION=develop
|
||||||
|
WORKFLOWS_IMAGES_VERSION=develop
|
||||||
|
CONTAINER_REGISTRY=cr.yandex/crp3ccidau046kdj8g9q
|
||||||
|
WORKFLOWS_CONFIG_FILEPATH=.example.tasks_execution_config.json
|
||||||
|
|
||||||
|
# --- Штампы/маркировки: HTTP или RabbitMQ ------------------------------------
|
||||||
|
USE_MARKS_RABBITMQ=0 # 0 — ходить в MARKS_PROCESSING_URL по HTTP; 1 — через RabbitMQ
|
||||||
|
MARKS_RABBITMQ_HOST=
|
||||||
|
MARKS_RABBITMQ_PORT=
|
||||||
|
MARKS_RABBITMQ_USER=
|
||||||
|
MARKS_RABBITMQ_PASSWORD=
|
||||||
|
MARKS_RABBITMQ_API=
|
||||||
|
|
||||||
|
# --- Rate limit для /documents/metadata --------------------------------------
|
||||||
|
METADATA_RATE_LIMIT_ENABLED=true
|
||||||
|
METADATA_RATE_LIMIT_MAX_REQUESTS=10
|
||||||
|
|
||||||
|
# --- Почта (флаги; сама отправка идёт через внешние сервисы) ------------------
|
||||||
|
ENABLE_MAILGUN=true
|
||||||
|
ENABLE_SMTP=false
|
||||||
|
|
||||||
|
# --- Наблюдаемость -----------------------------------------------------------
|
||||||
|
ENVIRONMENT=stage
|
||||||
|
SENTRY_DSN=
|
||||||
|
SENTRY_DEBUG=0
|
||||||
|
NAMESPACE=local
|
||||||
|
ENABLE_SQL_QUERY=1 # логировать SQL-запросы (для локальной отладки)
|
||||||
|
TRACER_USE=false
|
||||||
|
TRACER_HOST=localhost:4317
|
||||||
|
TRACER_USE_INSECURE=true
|
||||||
|
TRACER_LOGGER_NAME=tracer_logger
|
||||||
|
SERVICE_NAME=documentations-api
|
||||||
|
SERVICE_NAME_FILESTREAM=filestream-api
|
||||||
|
|
||||||
|
# --- Только для интеграционных тестов (кодом приложения не читается) ----------
|
||||||
|
# KEY_JWT=example_jwt
|
||||||
785
apps/documentations/api.openapi.yaml
Normal file
785
apps/documentations/api.openapi.yaml
Normal file
@ -0,0 +1,785 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Documentations API
|
||||||
|
version: "1.0"
|
||||||
|
description: |
|
||||||
|
REST-API сервиса документаций (`documentation-api`, бинарник `cmd/api`, образ
|
||||||
|
`documentations`, deployment `documentations-api` в неймспейсе `documentations`).
|
||||||
|
|
||||||
|
Спецификация реконструирована из исходного кода маршрутов
|
||||||
|
(`cmd/api/routes_api.go`, `cmd/api/routes_internal.go`) и middleware
|
||||||
|
(`cmd/api/bootstrap.go`, `pkg/midleware/*`). В репозитории **нет сгенерированного
|
||||||
|
swagger/openapi**, поэтому схемы тел запросов/ответов приведены обобщённо
|
||||||
|
(в коде они не описаны декларативно). Пути, методы и параметры пути —
|
||||||
|
достоверные, из роутера `gorilla/mux`.
|
||||||
|
|
||||||
|
## Базовые пути
|
||||||
|
- Публичный API: `/api/v1` (описан ниже).
|
||||||
|
- Внутренний API: `/internal/v1` (сервис-к-сервису, облегчённая авторизация;
|
||||||
|
здесь не детализируется — см. `cmd/api/routes_internal.go`).
|
||||||
|
- Потоковая отдача/приём файлов вынесены в **отдельный сервис `filestream`**
|
||||||
|
(`cmd/filestreamer`, образ `documentations-api-files`): `/api/v1/bundles/...`,
|
||||||
|
`/api/v1/documents/...`, `/api/v1/pages/...`, `/api/v1/documents/folders`,
|
||||||
|
`/api/v1/bundles_mrpas/...`, `/api/v1/public_link_mrpas/...`.
|
||||||
|
- Health-check: `GET /ping` (предоставляется каркасом роутера `rest`).
|
||||||
|
- Профилирование: `GET /debug/pprof/...` (net/http/pprof).
|
||||||
|
|
||||||
|
## Аутентификация
|
||||||
|
Основной способ — JWT в заголовке `Authorization: Bearer <token>`, проверяемый по
|
||||||
|
RSA-публичному ключу (`PUBLIC_KEY`, PEM/PKIX). Из claims извлекаются `company_ids`
|
||||||
|
и `service_accounts`. Опционально включается проверка через Zitadel (`USE_ZITADEL`),
|
||||||
|
а также разбор заголовка `Identity`. Часть ручек (пути `/public/...`,
|
||||||
|
`/public_link_mrpas/...` и запросы с `download_type=temporary`) авторизуются по
|
||||||
|
HMAC-JWT публичных ссылок (`DOCUMENT_PUBLIC_LINK_JWT_SECRET`). В сервисе filestream
|
||||||
|
ссылки скачивания дополнительно подписываются (`signature` + `expires_at` в query,
|
||||||
|
секрет `SIGNATURE_SECRET_KEY`).
|
||||||
|
|
||||||
|
## Формат ошибок
|
||||||
|
Ответы оборачиваются middleware `rest.JSONResponse`; ошибки возвращаются в JSON.
|
||||||
|
Нестандартный код `523` (`StatusOriginIsUnreachable`, `network/consts.go`)
|
||||||
|
используется, когда исходный сервис недоступен.
|
||||||
|
|
||||||
|
## Пагинация
|
||||||
|
Единого декларативного механизма пагинации в роутере нет; списки, где она нужна,
|
||||||
|
принимают параметры фильтрации в теле POST-запроса (напр. `/documents/metadata`,
|
||||||
|
`/documents/batch`). Эндпоинт `/documents/metadata` дополнительно ограничивается
|
||||||
|
rate-limit (`METADATA_RATE_LIMIT_*`).
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://api.sarex.io/documentations/api/v1
|
||||||
|
description: production
|
||||||
|
- url: https://api.preprod.sarex.io/documentations/api/v1
|
||||||
|
description: preprod
|
||||||
|
- url: https://stage-api.sarex.io/documentations/api/v1
|
||||||
|
description: stage
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: disks
|
||||||
|
- name: documents
|
||||||
|
- name: bundles
|
||||||
|
- name: uploads
|
||||||
|
- name: permissions
|
||||||
|
- name: workspaces
|
||||||
|
- name: dashboards
|
||||||
|
- name: workflows
|
||||||
|
- name: pages
|
||||||
|
- name: marks
|
||||||
|
- name: public-links
|
||||||
|
- name: related-documents
|
||||||
|
- name: changelogs
|
||||||
|
- name: favorite-documents
|
||||||
|
- name: name-templates
|
||||||
|
- name: misc
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/conversion:
|
||||||
|
post:
|
||||||
|
tags: [misc]
|
||||||
|
summary: Запустить конвертацию документа
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/disks:
|
||||||
|
get:
|
||||||
|
tags: [disks]
|
||||||
|
summary: Список дисков (доступных пользователю)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
post:
|
||||||
|
tags: [disks]
|
||||||
|
summary: Создать диск (требуются права администратора)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"403": { $ref: "#/components/responses/Forbidden" }
|
||||||
|
/disks/{disk_id}:
|
||||||
|
delete:
|
||||||
|
tags: [disks]
|
||||||
|
summary: Удалить диск (требуются права администратора)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"403": { $ref: "#/components/responses/Forbidden" }
|
||||||
|
/disks/{disk_id}/documents:
|
||||||
|
get:
|
||||||
|
tags: [disks, documents]
|
||||||
|
summary: Документы диска
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
post:
|
||||||
|
tags: [disks, documents]
|
||||||
|
summary: Документы диска по списку id
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/disks/{disk_id}/projects:
|
||||||
|
get:
|
||||||
|
tags: [disks]
|
||||||
|
summary: Проекты диска
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/disks/{disk_id}/service_accounts:
|
||||||
|
get:
|
||||||
|
tags: [disks, permissions]
|
||||||
|
summary: Сервисные аккаунты диска
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/disks/{disk_id}/size_migration:
|
||||||
|
get:
|
||||||
|
tags: [misc]
|
||||||
|
summary: Миграция размеров (служебное)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/disks/{disk_id}/delete_documents_from_ws_migration:
|
||||||
|
get:
|
||||||
|
tags: [misc]
|
||||||
|
summary: Удаление документов при миграции воркспейса (служебное)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DiskId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/documents:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Создать документ/папку
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
delete:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Массовое удаление документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/create_report:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Сформировать отчёт по метаданным документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/metadata:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Список метаданных документов (rate-limited)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"429": { $ref: "#/components/responses/TooManyRequests" }
|
||||||
|
/documents/batch:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Пакетное получение документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/flows:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Документы в трансмиттале/ревью
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/public_link:
|
||||||
|
post:
|
||||||
|
tags: [public-links]
|
||||||
|
summary: Создать публичную ссылку на документ
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/public/documents/public_link/{id}:
|
||||||
|
get:
|
||||||
|
tags: [public-links]
|
||||||
|
summary: Прочитать публичную ссылку (публичный доступ по HMAC-JWT)
|
||||||
|
security: []
|
||||||
|
parameters: [{ $ref: "#/components/parameters/StrId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/public_link/{id}:
|
||||||
|
patch:
|
||||||
|
tags: [public-links]
|
||||||
|
summary: Обновить публичную ссылку
|
||||||
|
parameters: [{ $ref: "#/components/parameters/StrId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
delete:
|
||||||
|
tags: [public-links]
|
||||||
|
summary: Удалить публичную ссылку
|
||||||
|
parameters: [{ $ref: "#/components/parameters/StrId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/update-path:
|
||||||
|
patch:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Сменить родителя документа
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/update-path:
|
||||||
|
patch:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Массовая смена родителя документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/super_create:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Создание документа суперпользователем (требуются права администратора)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"403": { $ref: "#/components/responses/Forbidden" }
|
||||||
|
/documents/types:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Справочник типов документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/get_folders_download_url:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Ссылка на скачивание папок (подписанная)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/download_url/documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Ссылка на скачивание документа
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/name_template:
|
||||||
|
get:
|
||||||
|
tags: [documents, name-templates]
|
||||||
|
summary: Шаблон имени документа
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Документ по id
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
"404": { $ref: "#/components/responses/NotFound" }
|
||||||
|
patch:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Переименовать/изменить документ (числовой id)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentIdNum" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
delete:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Удалить документ (числовой id)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentIdNum" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/bundles:
|
||||||
|
get:
|
||||||
|
tags: [documents, bundles]
|
||||||
|
summary: Бандлы документа
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/add_bundle:
|
||||||
|
post:
|
||||||
|
tags: [documents, bundles]
|
||||||
|
summary: Привязать бандл к документу
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/move_bundles:
|
||||||
|
patch:
|
||||||
|
tags: [documents, bundles]
|
||||||
|
summary: Переместить бандлы
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/ancestors:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Предки документа
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/filetypes_by_extension:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Определить тип файла по расширению
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/copy:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Копировать документы (долгая операция, таймаут 120 мин)
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/copy_structure:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Копировать структуру папок
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/bin:
|
||||||
|
delete:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Окончательно удалить документы из корзины
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/bin/restore:
|
||||||
|
patch:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Восстановить документы из корзины
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_ids}/company:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Компания документов
|
||||||
|
parameters:
|
||||||
|
- name: document_ids
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/permissions:
|
||||||
|
get:
|
||||||
|
tags: [permissions]
|
||||||
|
summary: Права доступа документа
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
post:
|
||||||
|
tags: [permissions]
|
||||||
|
summary: Выдать права на документ
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/documents/{document_id}/download:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Скачать документ (отдаётся сервисом filestream)
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/permissions:
|
||||||
|
get:
|
||||||
|
tags: [permissions]
|
||||||
|
summary: Справочник прав доступа
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/bundles:
|
||||||
|
post:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Создать бандл
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Бандл по id
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
patch:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Изменить бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
delete:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Удалить бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/download:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Скачать бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/{bundle_key}/download:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Скачать файл бандла по ключу
|
||||||
|
parameters:
|
||||||
|
- { $ref: "#/components/parameters/BundleId" }
|
||||||
|
- { $ref: "#/components/parameters/BundleKey" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/{bundle_key}/upload_single:
|
||||||
|
post:
|
||||||
|
tags: [bundles, uploads]
|
||||||
|
summary: Загрузить файл целиком (single upload)
|
||||||
|
parameters:
|
||||||
|
- { $ref: "#/components/parameters/BundleId" }
|
||||||
|
- { $ref: "#/components/parameters/BundleKey" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/{bundle_key}/upload_multipart:
|
||||||
|
post:
|
||||||
|
tags: [bundles, uploads]
|
||||||
|
summary: Начать multipart-загрузку файла бандла
|
||||||
|
parameters:
|
||||||
|
- { $ref: "#/components/parameters/BundleId" }
|
||||||
|
- { $ref: "#/components/parameters/BundleKey" }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/upload_finish:
|
||||||
|
post:
|
||||||
|
tags: [bundles, uploads]
|
||||||
|
summary: Завершить загрузку бандла
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/marks:
|
||||||
|
put:
|
||||||
|
tags: [marks]
|
||||||
|
summary: Добавить штампы/QR/подписи в бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/sign:
|
||||||
|
post:
|
||||||
|
tags: [bundles, marks]
|
||||||
|
summary: Подписать бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/restart:
|
||||||
|
post:
|
||||||
|
tags: [bundles, workflows]
|
||||||
|
summary: Перезапустить workflow бандла
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/cancel_qr:
|
||||||
|
patch:
|
||||||
|
tags: [marks]
|
||||||
|
summary: Отменить QR-код
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/comment:
|
||||||
|
patch:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Обновить комментарий бандла
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/presigned_url:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Presigned URL бандла
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/mrpas:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: MRPA бандла
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundles/{bundle_id}/copy:
|
||||||
|
post:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Копировать бандл
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/bundle/version:
|
||||||
|
get:
|
||||||
|
tags: [bundles]
|
||||||
|
summary: Версии бандла
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/uploads/multipart/{upload_id}/complete:
|
||||||
|
post:
|
||||||
|
tags: [uploads]
|
||||||
|
summary: Завершить multipart-загрузку
|
||||||
|
parameters: [{ $ref: "#/components/parameters/UploadId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/uploads/multipart/{upload_id}/abort:
|
||||||
|
post:
|
||||||
|
tags: [uploads]
|
||||||
|
summary: Прервать multipart-загрузку
|
||||||
|
parameters: [{ $ref: "#/components/parameters/UploadId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/uploads/multipart/{upload_id}/{part_num}:
|
||||||
|
post:
|
||||||
|
tags: [uploads]
|
||||||
|
summary: Загрузить часть (part) файла
|
||||||
|
parameters:
|
||||||
|
- { $ref: "#/components/parameters/UploadId" }
|
||||||
|
- name: part_num
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: integer }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/workspaces:
|
||||||
|
post:
|
||||||
|
tags: [workspaces]
|
||||||
|
summary: Создать воркспейс
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/workspaces/{ws_id}:
|
||||||
|
get:
|
||||||
|
tags: [workspaces]
|
||||||
|
summary: Документ воркспейса
|
||||||
|
parameters:
|
||||||
|
- name: ws_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/dashboards:
|
||||||
|
post:
|
||||||
|
tags: [dashboards]
|
||||||
|
summary: Создать дашборд
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/dashboards/{db_id}:
|
||||||
|
get:
|
||||||
|
tags: [dashboards]
|
||||||
|
summary: Документ дашборда
|
||||||
|
parameters:
|
||||||
|
- name: db_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/workflows/{workflow_id}:
|
||||||
|
get:
|
||||||
|
tags: [workflows]
|
||||||
|
summary: Workflow по id
|
||||||
|
parameters:
|
||||||
|
- name: workflow_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/pages:
|
||||||
|
post:
|
||||||
|
tags: [pages]
|
||||||
|
summary: Создать страницу
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/pages/{data_source}/{page_key}/download:
|
||||||
|
get:
|
||||||
|
tags: [pages]
|
||||||
|
summary: Скачать страницу
|
||||||
|
parameters:
|
||||||
|
- name: data_source
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
- name: page_key
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/public/qr/{public_uuid}/document_info:
|
||||||
|
get:
|
||||||
|
tags: [marks]
|
||||||
|
summary: Публичная информация о документе по QR
|
||||||
|
security: []
|
||||||
|
parameters:
|
||||||
|
- name: public_uuid
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string, format: uuid }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/related_documents:
|
||||||
|
post:
|
||||||
|
tags: [related-documents]
|
||||||
|
summary: Создать связь документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
get:
|
||||||
|
tags: [related-documents]
|
||||||
|
summary: Получить связанные документы
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/related_documents/bulk_delete:
|
||||||
|
post:
|
||||||
|
tags: [related-documents]
|
||||||
|
summary: Массово удалить связи документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/templates/{bundle_id}:
|
||||||
|
get:
|
||||||
|
tags: [misc]
|
||||||
|
summary: Шаблон по бандлу
|
||||||
|
parameters: [{ $ref: "#/components/parameters/BundleId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/changelogs/create:
|
||||||
|
post:
|
||||||
|
tags: [changelogs]
|
||||||
|
summary: Создать changelog
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/changelogs/{changelog_id}:
|
||||||
|
patch:
|
||||||
|
tags: [changelogs]
|
||||||
|
summary: Обновить changelog
|
||||||
|
parameters:
|
||||||
|
- name: changelog_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/links:
|
||||||
|
post:
|
||||||
|
tags: [misc]
|
||||||
|
summary: Создать ссылку
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/favorite_documents:
|
||||||
|
post:
|
||||||
|
tags: [favorite-documents]
|
||||||
|
summary: Добавить документ в избранное
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
get:
|
||||||
|
tags: [favorite-documents]
|
||||||
|
summary: Список избранных документов
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/favorite_documents/{document_id}:
|
||||||
|
delete:
|
||||||
|
tags: [favorite-documents]
|
||||||
|
summary: Убрать документ из избранного
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
/name_templates/create:
|
||||||
|
post:
|
||||||
|
tags: [name-templates]
|
||||||
|
summary: Создать шаблон имени
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
/name_templates/{document_id}:
|
||||||
|
get:
|
||||||
|
tags: [name-templates]
|
||||||
|
summary: Шаблон имени по документу
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
patch:
|
||||||
|
tags: [name-templates]
|
||||||
|
summary: Обновить шаблон имени
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
delete:
|
||||||
|
tags: [name-templates]
|
||||||
|
summary: Удалить шаблон имени
|
||||||
|
parameters: [{ $ref: "#/components/parameters/DocumentId" }]
|
||||||
|
responses:
|
||||||
|
"200": { $ref: "#/components/responses/Ok" }
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
description: >-
|
||||||
|
JWT, подписанный ключом, соответствующим `PUBLIC_KEY` (RSA/PKIX).
|
||||||
|
Для публичных ссылок используется HMAC-JWT (`DOCUMENT_PUBLIC_LINK_JWT_SECRET`).
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
DiskId:
|
||||||
|
name: disk_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
DocumentId:
|
||||||
|
name: document_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
DocumentIdNum:
|
||||||
|
name: document_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: Числовой идентификатор документа (маршрут ограничен regex `[0-9]+`)
|
||||||
|
schema: { type: integer }
|
||||||
|
BundleId:
|
||||||
|
name: bundle_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string, format: uuid }
|
||||||
|
BundleKey:
|
||||||
|
name: bundle_key
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
UploadId:
|
||||||
|
name: upload_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
StrId:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
|
||||||
|
responses:
|
||||||
|
Ok:
|
||||||
|
description: Успешный ответ (тело зависит от ручки; в JSON)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { type: object, additionalProperties: true }
|
||||||
|
Forbidden:
|
||||||
|
description: Недостаточно прав
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: "#/components/schemas/Error" }
|
||||||
|
NotFound:
|
||||||
|
description: Ресурс не найден
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: "#/components/schemas/Error" }
|
||||||
|
TooManyRequests:
|
||||||
|
description: Превышен лимит запросов (rate limit)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: "#/components/schemas/Error" }
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
additionalProperties: true
|
||||||
156
apps/documentations/dps-message-hub.CONFIGURATION.md
Normal file
156
apps/documentations/dps-message-hub.CONFIGURATION.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
# Конфигурация проекта dps-message-hub
|
||||||
|
# Версия: 0.1.0
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса.
|
||||||
|
|
||||||
|
`dps-message-hub` (`dps_message_hub`) — это Kafka-воркер на базе [FastStream](https://faststream.airt.ai/), потребляющий сообщения об изменении ассетов и обновляющий данные разметки (`markup_event`) в PostgreSQL домена «documentations». HTTP API у сервиса нет.
|
||||||
|
|
||||||
|
> Это отдельный сервис, не путать с приложением `message-hub` (домен `planning`): у них разные префиксы переменных, набор интеграций и назначение.
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `src/dps_message_hub/infra/config.py` через библиотеку [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) (класс `AppSettings(BaseSettings)`).
|
||||||
|
|
||||||
|
Особенности разбора (`SettingsConfigDict`):
|
||||||
|
|
||||||
|
- `env_prefix="DPS_MESSAGE_HUB_"` — все переменные приложения начинаются с этого префикса;
|
||||||
|
- `env_nested_delimiter="__"` — вложенные секции задаются двойным подчёркиванием, напр. `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST` → `documentation_db.host`;
|
||||||
|
- `env_ignore_empty=False` — пустая строка считается заданным значением (не игнорируется);
|
||||||
|
- `frozen=True` — объект настроек неизменяем после инициализации.
|
||||||
|
|
||||||
|
Настройки разбиты на три вложенные секции (модели `pydantic.BaseModel`), читаемые одним классом `AppSettings`:
|
||||||
|
|
||||||
|
- `app` (`App`) — префикс `DPS_MESSAGE_HUB_APP__`;
|
||||||
|
- `documentation_db` (`Database`) — префикс `DPS_MESSAGE_HUB_DOCUMENTATION_DB__`;
|
||||||
|
- `kafka` (`Kafka`) — префикс `DPS_MESSAGE_HUB_KAFKA__`.
|
||||||
|
|
||||||
|
Класс `Settings` (`src/dps_message_hub/infra/config.py`) — синглтон (`wiring.SingletonMeta`) поверх `AppSettings`, отдаёт настройки через свойство `.settings`.
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет. У классов настроек **не задан** `env_file`, поэтому файл `.env` автоматически не загружается — переменные нужно экспортировать в окружение процесса самостоятельно (`make config` лишь копирует `.example.env` → `.env` как шаблон), либо пробрасывать их в контейнер через `--env-file` (см. `Makefile`, цель `container-run`).
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса. `make config` копирует `.example.env` → `.env`, но приложение **не загружает `.env` автоматически** — экспортируйте сами, напр. `set -a && . ./.env && set +a` |
|
||||||
|
| Локально (контейнер) | `Makefile`: цель `container-run` пробрасывает переменные через `--env-file .env` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`: блок `universal-chart.services.api.envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов); базовый чарт — `universal-chart` (`oci://.../charts`, версия `0.1.7`) |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна (`workflow.rules`) и `HELM_SET_ARGS` |
|
||||||
|
|
||||||
|
Запуск процесса (`scripts/entrypoint.sh`): единый FastStream-процесс с брокером Kafka:
|
||||||
|
|
||||||
|
```
|
||||||
|
faststream run \
|
||||||
|
--factory \
|
||||||
|
--workers ${DPS_MESSAGE_HUB_NUM_WORKERS} \
|
||||||
|
'dps_message_hub.infra.app:get_app'
|
||||||
|
```
|
||||||
|
|
||||||
|
Фабрика `dps_message_hub.infra.app:get_app` (`src/dps_message_hub/infra/app.py`) собирает `FastStream`-приложение: создаёт `KafkaBroker`, подключает роутер-потребитель топика `assets` и открывает пул соединений PostgreSQL в lifespan. Отдельных точек входа для воркеров/крон-задач нет — `pyproject.toml` не содержит `[project.scripts]`.
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что значение обязательно (иначе ошибка старта настроек).
|
||||||
|
|
||||||
|
### Приложение (`DPS_MESSAGE_HUB_APP__*`) — класс `App`
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DPS_MESSAGE_HUB_APP__LOG_LEVEL` | enum (`LogLevel`) | `INFO` | Уровень логирования. Допустимо: `CRITICAL`/`FATAL`/`ERROR`/`WARNING`/`WARN`/`INFO`/`DEBUG`/`NOTSET` |
|
||||||
|
| `DPS_MESSAGE_HUB_APP__IS_DEV` | bool | `False` | Признак dev-режима. Поле объявлено в настройках, но в текущем коде не используется |
|
||||||
|
| `DPS_MESSAGE_HUB_APP__BROKER_TYPE` | enum (`BrokerType`) | `—` (обязательно) | Тип брокера сообщений. Поддерживается только значение `kafka` |
|
||||||
|
|
||||||
|
### База данных PostgreSQL (`DPS_MESSAGE_HUB_DOCUMENTATION_DB__*`) — класс `Database`
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST` | string | `—` | Хост PostgreSQL |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PORT` | int | `—` | Порт PostgreSQL |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__USER` | string | `—` | Пользователь БД |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PASSWORD` | string | `—` | Пароль пользователя БД |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__NAME` | string | `—` | Имя базы данных |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__ENABLE_SSL` | bool | `—` | Включить TLS-подключение к БД |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_MODE` | enum (`verify-full`/`verify-ca`/`""`) | `—` | Режим проверки TLS (параметр `sslmode` DSN). При `ENABLE_SSL=true` не должно быть пустым |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_ROOT_CERT_PATH` | string | `—` | Путь к CA-сертификату (параметр `sslrootcert` DSN). При `ENABLE_SSL=true` не должно быть пустым |
|
||||||
|
|
||||||
|
Валидатор `check_ssl_options_configured_when_ssl_enabled`: если `ENABLE_SSL=true`, то `SSL_MODE` и `SSL_ROOT_CERT_PATH` обязаны быть непустыми, иначе ошибка старта. Итоговый DSN собирается в вычисляемом поле `documentation_db.uri` (`postgresql://user:password@host:port/name`, при SSL добавляются `?sslmode=...&sslrootcert=...`).
|
||||||
|
|
||||||
|
### Kafka (`DPS_MESSAGE_HUB_KAFKA__*`) — класс `Kafka`
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__HOST` | string | `—` | Хост брокера Kafka |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__PORT` | int | `—` | Порт брокера Kafka |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__USERNAME` | string | `—` | Логин SASL (`SCRAM-SHA-512`) |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__PASSWORD` | string | `—` | Пароль SASL (`SCRAM-SHA-512`) |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` | string \| null | `None` | Путь к CA-сертификату для SSL-контекста. Если задан — используется `SASL_SSL`, иначе SASL без TLS |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__TOPICS` | dict (JSON) → `KafkaTopics` | `—` | Соответствие логического топика реальному имени в Kafka. Обязателен ключ `assets`, напр. `{"assets": "assets_broadcast"}` |
|
||||||
|
|
||||||
|
Формирование параметров подключения (`src/dps_message_hub/infra/app.py`): адрес брокера — вычисляемое поле `kafka.uri` (`host:port`). Безопасность через `faststream.security.SASLScram512`:
|
||||||
|
|
||||||
|
- если заданы `USERNAME` и `PASSWORD` и `SSL_CAFILE` пуст — `SASLScram512(..., use_ssl=False)`;
|
||||||
|
- если задан `SSL_CAFILE` — `SASLScram512(..., ssl_context=create_ssl_context(cafile=...))`.
|
||||||
|
|
||||||
|
## Переменные инфраструктуры, сборки и запуска
|
||||||
|
|
||||||
|
Не читаются классами настроек приложения, но участвуют в запуске/сборке/деплое.
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DPS_MESSAGE_HUB_NUM_WORKERS` | `scripts/entrypoint.sh`, Helm `envs` | Число воркеров FastStream (`faststream run --workers`). В Helm `_default: '2'` |
|
||||||
|
| `DPS_MESSAGE_HUB_PYTHON_IMAGE_NAME` | `Dockerfile` (ARG) | Базовый образ Python (по умолчанию `python`) |
|
||||||
|
| `DPS_MESSAGE_HUB_PYTHON_IMAGE_TAG` | `Dockerfile` (ARG) | Тег базового образа (по умолчанию `3.13-slim`) |
|
||||||
|
| `PIP_INDEX_URL`, `PIP_TRUSTED_HOST` | `Dockerfile` (ARG) | Индекс/доверенный хост pip при сборке |
|
||||||
|
|
||||||
|
Сборка (`Dockerfile`): многостадийная — стадия `builder` собирает wheel'ы из `requirements/requirements.txt`, стадия `runner` ставит их, копирует `src/` и устанавливает пакет (`pip install . --no-deps`); процесс запускается непривилегированным пользователем `dps_message_hub`. Целевая версия Python — `3.13` (`.python-version`, `requires-python >=3.13`).
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Сервис деплоится через зависимость `universal-chart` (`.helm/Chart.yaml`, версия `0.1.7`). Настраивается один сервис — `services.api` (тип нагрузки — deployment, `replicaCount._default: 1`). HTTP-`service` и `ingress` отключены, health-пробы (`liveness`/`readiness`, путь `/ping`) — `enabled: false` (у сервиса нет HTTP-эндпоинтов).
|
||||||
|
|
||||||
|
Обычные значения (блок `services.api.envs`) различаются по окружениям (`_default`/`stage`/`preprod`/`production`) адресами БД и Kafka, именем БД и именами топиков. В Helm дополнительно заданы (отсутствуют в `.example.env`): `DPS_MESSAGE_HUB_KAFKA__HOST`, `DPS_MESSAGE_HUB_KAFKA__PORT` (`9091`), `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` (`/opt/config/ca.crt`), `DPS_MESSAGE_HUB_DOCUMENTATION_DB__ENABLE_SSL=true`, `SSL_MODE=verify-full`, `SSL_ROOT_CERT_PATH=/opt/config/ca.crt`.
|
||||||
|
|
||||||
|
Значения из секретов (блок `secretEnvs`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`_default` / `stage`) | Ключ (`secretKey`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__USER` | `ya-pg-secret` / `documentations-postgresql-secret` | `username` |
|
||||||
|
| `DPS_MESSAGE_HUB_DOCUMENTATION_DB__PASSWORD` | `ya-pg-secret` / `documentations-postgresql-secret` | `password` |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__USERNAME` | `kafka-secret` | `username` |
|
||||||
|
| `DPS_MESSAGE_HUB_KAFKA__PASSWORD` | `kafka-secret` | `password` |
|
||||||
|
|
||||||
|
Помимо env, чарт монтирует CA-сертификат Яндекса из `ConfigMap` `ya-ca-cert` (ключ `ca.crt`) как файл `/opt/config/ca.crt` (том `cm-ya-ca-cert`, `readOnly`) — на него указывают `DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE` и `DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_ROOT_CERT_PATH`.
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | NAMESPACE | CHART_VERSION | K8S_HUSTLER_BRANCH |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| MR (`merge_request_event`) | — | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||||||
|
| ветка `stage` | `stage` | `documentations` | `0.0.1-stage` | `stage` |
|
||||||
|
| ветка `master` | `preprod` | `documentations-preprod` | `0.0.1-preprod` | `preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `0.0.1-prod` | `master` |
|
||||||
|
|
||||||
|
Ключевые переменные пайплайна: `SERVICE_NAME=dps-message-hub`, `DOCKERFILE_PATH=Dockerfile`, `CI_TRIGGER_SOURCE=app`, `RELEASE_NAME=dps-message-hub`, `CHART_NAME=dps-message-hub`, флаги `ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_DEPLOY`, `HELM_SET_ARGS` (`--set universal-chart...`). Отдельные job'ы `format` (`ruff format --diff`) и `lint` (`ruff check`) на образе `python:3.13-slim`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- Приложение **не читает `.env`** автоматически (в `SettingsConfigDict` нет `env_file`). `make config` только создаёт `.env` из шаблона — переменные нужно экспортировать самому либо передавать контейнеру через `--env-file`.
|
||||||
|
- Большинство полей БД и Kafka **обязательны** (без дефолтов): при пустом окружении настройки не пройдут валидацию и сервис не стартует. Единственные необязательные — `APP__LOG_LEVEL`, `APP__IS_DEV`, `KAFKA__SSL_CAFILE`.
|
||||||
|
- `DPS_MESSAGE_HUB_APP__BROKER_TYPE` обязателен; поддерживается только `kafka` (иных веток в `match` нет). При другом значении брокер не будет создан.
|
||||||
|
- При `ENABLE_SSL=true` обязательно задавать `SSL_MODE` и `SSL_ROOT_CERT_PATH`, иначе валидатор настроек прервёт старт.
|
||||||
|
- `DPS_MESSAGE_HUB_KAFKA__TOPICS` обязан содержать ключ `assets` (модель `KafkaTopics`); прочие ключи игнорируются, отсутствие `assets` — ошибка старта.
|
||||||
|
- Поле `APP__IS_DEV` присутствует в настройках, но в текущем коде нигде не задействовано.
|
||||||
|
- Обработка Kafka-сообщений идёт с `auto_commit=False` и middleware `Retry` (`src/dps_message_hub/interface/middleware.py`), которая повторяет обработку **бесконечно** с экспоненциальной задержкой (до `1<<10 = 1024` сек) — «отравленное» сообщение может заблокировать партицию.
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Kafka и Zookeeper поднимаются через `Makefile` (цели `run-deps`/`run-zookeeper`/`run-kafka`); PostgreSQL — внешний. Минимально нужно задать:
|
||||||
|
|
||||||
|
- `DPS_MESSAGE_HUB_APP__BROKER_TYPE=kafka`;
|
||||||
|
- `DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST`, `__PORT`, `__USER`, `__PASSWORD`, `__NAME`, `__ENABLE_SSL` (для локали `false`), `__SSL_MODE`, `__SSL_ROOT_CERT_PATH` (при `ENABLE_SSL=false` можно пустыми);
|
||||||
|
- `DPS_MESSAGE_HUB_KAFKA__HOST`, `__PORT`, `__USERNAME`, `__PASSWORD`, `__TOPICS` (JSON с ключом `assets`);
|
||||||
|
- `DPS_MESSAGE_HUB_NUM_WORKERS` (для `entrypoint.sh`).
|
||||||
|
|
||||||
|
Локальный запуск: `make run` (через `entrypoint.sh`) либо `make run-dev` (`faststream run --factory --reload dps_message_hub.infra.app:get_app`). Готовые значения-примеры приведены в `dps-message-hub.env.example` рядом с этим документом.
|
||||||
45
apps/documentations/dps-message-hub.ENDPOINTS.md
Normal file
45
apps/documentations/dps-message-hub.ENDPOINTS.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Интерфейсы сервиса dps-message-hub
|
||||||
|
# Версия: 0.1.0
|
||||||
|
|
||||||
|
Документ описывает интерфейсную поверхность сервиса: потребляемые топики Kafka и обращения к внешним зависимостям (PostgreSQL).
|
||||||
|
|
||||||
|
> `dps-message-hub` — это чистый Kafka-воркер на [FastStream](https://faststream.airt.ai/). У него **нет HTTP/REST API** (в коде нет FastAPI/Flask/aiohttp, health-роутов и т.п.), поэтому файла `openapi.yaml` для сервиса нет. Исходящих HTTP-запросов к другим сервисам он тоже не выполняет — единственный получатель данных — база PostgreSQL.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Единое FastStream-приложение (`dps_message_hub.infra.app:get_app`, `src/dps_message_hub/infra/app.py`) объединяет:
|
||||||
|
|
||||||
|
- **Kafka** — потребитель сообщений (`KafkaBroker` + `KafkaRouter`), топик `assets` (`src/dps_message_hub/features/assets/interface/kafka.py`);
|
||||||
|
- **PostgreSQL** — пул соединений `psycopg` (`AsyncConnectionPool`), открывается/закрывается в lifespan (`src/dps_message_hub/infra/lifespan.py`, `infra/database.py`).
|
||||||
|
|
||||||
|
Каждое сообщение проходит через middleware `Retry` (`src/dps_message_hub/interface/middleware.py`): при исключении обработка повторяется бесконечно с экспоненциальной задержкой (`1 << min(10, retry_count)` секунд). Коммит оффсета ручной — у потребителя `auto_commit=False`.
|
||||||
|
|
||||||
|
## Kafka-потребители (входящие сообщения)
|
||||||
|
|
||||||
|
Реальное имя топика задаётся переменной `DPS_MESSAGE_HUB_KAFKA__TOPICS` (маппинг логического имени `assets` в имя топика Kafka). Формат сообщения — `BrokerMessageDto` (`src/dps_message_hub/infra/dto.py`): поля `schema_version`, `model`, `sender`, `type`, `body`, `diff`, `timestamp`, `trace_id` (alias `xtraceId`), `user_id`, `tenants`, `tags`.
|
||||||
|
|
||||||
|
| Топик (логич.) | `group_id` | Offset reset | `auto_commit` | Обработчик | Назначение |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| `assets` | `dps_assets_consumer` | earliest | `false` | `assets` | Обновление событий разметки (`markup_event`) по изменению ассета |
|
||||||
|
|
||||||
|
Диспетчеризация внутри обработчика `assets` по полю `type` (`src/dps_message_hub/features/assets/interface/kafka.py`); `body` разбирается в модель `AssetUpdate` (поля `id`, `attributes[]`), `diff` — произвольный `dict`:
|
||||||
|
|
||||||
|
| `type` сообщения | Условие (`diff`) | Действие | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `model_updated` | `diff is None` | — | Пропуск (нет изменений) |
|
||||||
|
| `model_updated` | в `diff` есть ключ `resource_id` | `unlink_asset(asset_id)` | Отвязка ассета: деактивация его событий разметки |
|
||||||
|
| `model_updated` | в `diff` есть ключ `attributes` | `update_markup_events(asset_id, attributes)` | Пересоздание событий разметки по обновлённым атрибутам |
|
||||||
|
| `model_deleted` | — | `unlink_asset(asset_id)` | Отвязка ассета при удалении модели |
|
||||||
|
|
||||||
|
Бизнес-логика (`src/dps_message_hub/features/assets/application/asset.py`): для обновляемых атрибутов существующие активные `markup_event` деактивируются (`inactivation_time`), затем в транзакции вставляются новые записи. Тип атрибута (поле `type`) определяет целевую колонку значения (`value_int`/`value_float`/`value_string`/`value_option_id`/`unit_option_id`/`value_dt`/`value_date`) — маппинг в `features/assets/domain/entities.py`.
|
||||||
|
|
||||||
|
## Внешние зависимости (инфраструктура)
|
||||||
|
|
||||||
|
| Зависимость | Назначение |
|
||||||
|
| --- | --- |
|
||||||
|
| Kafka | Источник сообщений (топик `assets`). Подключение `SASLScram512`, при заданном `KAFKA__SSL_CAFILE` — по `SASL_SSL` |
|
||||||
|
| PostgreSQL | Хранилище данных (`psycopg` async, таблица `markup_event`). Операции: `SELECT`, `UPDATE`, массовая вставка `COPY ... FROM STDIN` (`features/assets/infra/database.py`) |
|
||||||
|
|
||||||
|
## Исходящие HTTP-запросы к внешним сервисам
|
||||||
|
|
||||||
|
Отсутствуют. Сервис не содержит HTTP-клиентов (`httpx`/`aiohttp`/`requests`) и не обращается к другим сервисам по HTTP; все побочные эффекты — запись в PostgreSQL.
|
||||||
42
apps/documentations/dps-message-hub.env.example
Normal file
42
apps/documentations/dps-message-hub.env.example
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# dps-message-hub — пример конфигурации (.env)
|
||||||
|
# Версия: 0.1.0
|
||||||
|
# Скопируйте в .env (`make config`) и заполните значения.
|
||||||
|
# ВНИМАНИЕ: приложение НЕ загружает .env автоматически (в классах настроек нет
|
||||||
|
# env_file/dotenv). Экспортируйте переменные сами, напр.:
|
||||||
|
# set -a && . ./.env && set +a
|
||||||
|
# либо запускайте контейнер через `--env-file .env` (см. Makefile).
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- Инфраструктура запуска (не читается классом настроек) ---
|
||||||
|
# Число воркеров faststream (scripts/entrypoint.sh: faststream run --workers)
|
||||||
|
DPS_MESSAGE_HUB_NUM_WORKERS=1
|
||||||
|
|
||||||
|
# --- Приложение (префикс DPS_MESSAGE_HUB_APP__) ---
|
||||||
|
# CRITICAL | FATAL | ERROR | WARNING | WARN | INFO | DEBUG | NOTSET
|
||||||
|
DPS_MESSAGE_HUB_APP__LOG_LEVEL=INFO
|
||||||
|
DPS_MESSAGE_HUB_APP__IS_DEV=true
|
||||||
|
# Тип брокера сообщений (поддерживается только kafka)
|
||||||
|
DPS_MESSAGE_HUB_APP__BROKER_TYPE=kafka
|
||||||
|
|
||||||
|
# --- База данных PostgreSQL (префикс DPS_MESSAGE_HUB_DOCUMENTATION_DB__) ---
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__HOST=
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__PORT=6432
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__USER=
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__PASSWORD=
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__NAME=
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__ENABLE_SSL=false
|
||||||
|
# verify-full | verify-ca | "" (обязателен при ENABLE_SSL=true)
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_MODE=
|
||||||
|
# Путь к CA-сертификату (обязателен при ENABLE_SSL=true)
|
||||||
|
DPS_MESSAGE_HUB_DOCUMENTATION_DB__SSL_ROOT_CERT_PATH=
|
||||||
|
|
||||||
|
# --- Kafka (префикс DPS_MESSAGE_HUB_KAFKA__) ---
|
||||||
|
DPS_MESSAGE_HUB_KAFKA__HOST=localhost
|
||||||
|
DPS_MESSAGE_HUB_KAFKA__PORT=9092
|
||||||
|
DPS_MESSAGE_HUB_KAFKA__USERNAME=
|
||||||
|
DPS_MESSAGE_HUB_KAFKA__PASSWORD=
|
||||||
|
# Путь к CA-сертификату для SSL-контекста (SASL_SSL). Если пусто — SASL без SSL
|
||||||
|
# DPS_MESSAGE_HUB_KAFKA__SSL_CAFILE=/opt/config/ca.crt
|
||||||
|
# Соответствие логического топика `assets` реальному имени топика Kafka (JSON)
|
||||||
|
DPS_MESSAGE_HUB_KAFKA__TOPICS='{"assets": "assets_broadcast_test"}'
|
||||||
66
apps/documentations/frontend.CONFIGURATION.md
Normal file
66
apps/documentations/frontend.CONFIGURATION.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Конфигурация проекта documentation-frontend
|
||||||
|
|
||||||
|
Документ описывает сборку, рантайм-конфигурацию и деплой микрофронтенда `documentation-frontend` (образ `documentation-frontend-app`, деплой `frontend` в домене `documentations`).
|
||||||
|
|
||||||
|
## Способ конфигурирования
|
||||||
|
|
||||||
|
Это фронтенд-модуль (Module Federation remote), поэтому в отличие от backend-сервисов он **не** читает переменные окружения в рантайме. Единственный параметр конфигурации, влияющий на поведение, — окружение сборки `BUILD_ENV`, которое webpack «зашивает» в бандл на этапе сборки.
|
||||||
|
|
||||||
|
- Значение берётся из `process.env.BUILD_ENV` при запуске webpack.
|
||||||
|
- Webpack подставляет его как глобальную константу `BUILD_ENV` через `DefinePlugin` (`webpack.config.js`).
|
||||||
|
- Допустимые значения проверяются в `env.js`: `local`/`stage`/`preprod`/`prod`/`contour`. Если значение не входит в набор — сборка падает с ошибкой.
|
||||||
|
- `build.config.js` по `BUILD_ENV` выбирает режим webpack (`mode`/`devtool`): `local`/`stage` → `development` + `eval-source-map`, `prod`/`preprod`/`contour` → `production` + `source-map`.
|
||||||
|
- В рантайме `BUILD_ENV` определяет базовые хосты сервисов (`module/api/hosts.ts`, `resolveHost`) и тип http-сервиса (`module/api/http-service.ts`: при `local` — `setTypeOfHttpService("original")`). Подробности по хостам и эндпоинтам — в `frontend.ENDPOINTS.md`.
|
||||||
|
|
||||||
|
## Переменные сборки
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `BUILD_ENV` | `env.js`, `build.config.js`, `webpack.config.js` (`DefinePlugin`), `Dockerfile` (`ARG`), `.gitlab-ci.yml` (`--build-arg`) | Окружение сборки: `local`/`stage`/`preprod`/`prod`/`contour`. Определяет режим сборки и базовые хосты API |
|
||||||
|
| `NPM_NEXUS_TOKEN` | `Dockerfile` (`ARG`), `.npmrc`, `.gitlab-ci.yml` (`--build-arg`) | Токен доступа к приватному npm-реестру (Nexus) при `npm i` |
|
||||||
|
|
||||||
|
Отдельного `.env`-файла в репозитории нет; переменные передаются как build-arg'и Docker/CI.
|
||||||
|
|
||||||
|
## Сборка (`package.json`, `Dockerfile`)
|
||||||
|
|
||||||
|
Скрипты npm:
|
||||||
|
|
||||||
|
| Скрипт | Команда | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `build-module` | `webpack --config webpack.config.js` | Сборка модуля в `dist` (используется в образе) |
|
||||||
|
| `serve-module` | `webpack serve --config webpack.config.js` | Dev-сервер (порт `9002`, https) |
|
||||||
|
| `lint` | `eslint ./module/**/*.ts(x) --fix` | Линтинг |
|
||||||
|
| `start` | `BUILD_ENV=local run-p serve-module storybook` | Локальный запуск (dev-сервер + Storybook) |
|
||||||
|
| `storybook` / `build-storybook` | `start-storybook` / `build-storybook` | Storybook |
|
||||||
|
|
||||||
|
Сборка образа (`Dockerfile`, multi-stage):
|
||||||
|
|
||||||
|
1. Стадия `static` (`node:16`): `npm i --legacy-peer-deps` с `NPM_NEXUS_TOKEN`, затем `npm run lint` и `BUILD_ENV=$BUILD_ENV npm run build-module` → артефакты в `/app/dist`.
|
||||||
|
2. Финальная стадия (`nginx:1.19.6`): копирует `dist` в `/dist` и `nginx/nginx.conf` в `/etc/nginx/nginx.conf`.
|
||||||
|
|
||||||
|
Module Federation (`webpack.config.js`, `ModuleFederationPlugin`): имя remote — `srx_documentations`, `filename: module/remoteEntry.js`. Экспонируемые модули: `./DocumentationsPage`, `./DocumentSelect`, `./CreateDocDialog`, `./DownloadFilesDialog`, `./FileBindingsDialog`. Shared-зависимости (singleton): `react`, `react-dom`, `@material-ui/core`, `@material-ui/styles`, `@sarex-team/sdk-js`, `@sarex-team/translator`, `@sarex-team/ui-kit`, `mobx`, `mobx-react-lite`.
|
||||||
|
|
||||||
|
## Деплой (Helm, `.helm/values.yaml`)
|
||||||
|
|
||||||
|
Чарт использует общий `universal-chart`. Ключевые значения для сервиса `frontend`:
|
||||||
|
|
||||||
|
- `deployment.name._default`: `documentation-frontend-static`; порт контейнера `80`.
|
||||||
|
- `replicaCount`: `stage` — 1, `preprod` — 2, `production` — 2.
|
||||||
|
- Пробы `liveness`/`readiness`: `httpGet /ping` на порту `80`.
|
||||||
|
- `resources.requests`: `memory 100Mi`, `cpu 100m`.
|
||||||
|
- `image.name._default`: `cr.yandex/crp3ccidau046kdj8g9q/documentation-frontend-static:latest` (в CI переопределяется на собранный `IMAGE_NAME` через `HELM_SET_ARGS`).
|
||||||
|
- `service`: `ClusterIP`, порт `80` (в `stage` — `8080`), `targetPort 80`, `portName http`.
|
||||||
|
- `imagePullSecrets.name._default`: `dockerhub`.
|
||||||
|
|
||||||
|
## CI/CD (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает шаблоны `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`). `SERVICE_NAME`: `documentation-frontend-app`. Окружение переключается по ветке/тегу (`workflow.rules`):
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace | `BUILD_ENV` | `global.env` | `CHART_VERSION` |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `documentations` | `stage` | `stage` | `0.0.1-stage` |
|
||||||
|
| ветка `master` | `preprod` | `documentations-preprod` | `preprod` | `preprod` | `0.0.1-preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `prod` | `production` | `0.0.1-prod` |
|
||||||
|
| merge request | — | — | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE: "false"`) |
|
||||||
|
|
||||||
|
Каждая ветка задаёт `BUILD_ARGS` (`--build-arg BUILD_ENV=... --build-arg NPM_NEXUS_TOKEN=...`) и `HELM_SET_ARGS` (образ, `global.env`, `commitSha`, `gitlabUri`, `gitlabJobUrl`, `owner`), а также `K8S_HUSTLER_BRANCH` соответствующего окружения (`universal-chart-stage`/`-preprod`/`-production`).
|
||||||
224
apps/documentations/frontend.ENDPOINTS.md
Normal file
224
apps/documentations/frontend.ENDPOINTS.md
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует documentation-frontend
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается модуль (микрофронтенд `documentation-frontend`, образ `documentation-frontend-app`, деплой `frontend`). Модуль публикуется как remote для Module Federation (`webpack.config.js`, имя `srx_documentations`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Все запросы описаны декларативно в реестре `module/api/endpoints.ts` (объект `endpoints`). Каждый эндпоинт задаётся структурой `Endpoint`:
|
||||||
|
|
||||||
|
- `service` — логическое имя сервиса (см. таблицу хостов ниже);
|
||||||
|
- `method` — HTTP-метод (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`);
|
||||||
|
- `path(args)` — функция, возвращающая путь запроса (с подстановкой параметров/query);
|
||||||
|
- `body(args)` — опционально, формирование тела запроса;
|
||||||
|
- `responseType` — опционально, тип ответа (напр. `blob`);
|
||||||
|
- `accessToken(args)` — опционально, явная передача access-токена в заголовки;
|
||||||
|
- `cache`, `queryOptions` — опции кеширования/повторов;
|
||||||
|
- `showErrorNotification` — показывать ли уведомление об ошибке (по умолчанию `true`).
|
||||||
|
|
||||||
|
Запрос выполняется единой функцией `fetch(endpoint, params, controller)` (`module/api/endpoints.ts`), которая через `httpService` (`module/api/http-service.ts`, обёртка `createHttpService` из `@sarex-team/sdk-js` поверх `axios`) отправляет запрос на базовый хост сервиса. Базовый хост выбирается по паре «`service` + окружение»: `httpService` создаётся с картой хостов `apiHosts` и текущим `buildEnv`, и разрешает хост внутри себя. Тот же алгоритм продублирован в экспортируемом хелпере `resolveHost(service)` из `module/api/hosts.ts`. Итоговый URL = `<базовый хост сервиса>` + `path` эндпоинта.
|
||||||
|
|
||||||
|
Окружение определяется глобальной константой `BUILD_ENV`, которую webpack подставляет в бандл через `DefinePlugin` (`webpack.config.js`) из переменной сборки `process.env.BUILD_ENV`. Допустимые значения проверяются в `env.js`: `local`/`stage`/`preprod`/`prod`/`contour`. В режиме `local` тип http-сервиса переключается на `"original"` (`module/api/http-service.ts`).
|
||||||
|
|
||||||
|
Ошибки маппируются в человекочитаемые сообщения в `module/api/errors.ts` (`resolveNetworkErrorByCode`, `resolveNetworkError`).
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значения из `module/api/hosts.ts` (объект `apiHosts`). Итоговый URL = `<базовый хост сервиса>` + `path` эндпоинта.
|
||||||
|
|
||||||
|
| Сервис (`service`) | Назначение | `local` | `stage` | `preprod` | `prod` | `contour` |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| `sarex` | Локальный сервис данных (`/api/core`, `/api/client`) | `/sarex-backend` | `/` | `/` | `/` | `/` |
|
||||||
|
| `documentations` | Сервис документации (документы, бандлы, файлы) | `https://stage-api.sarex.io/documentations` | `https://stage-api.sarex.io/documentations` | `https://api.preprod.sarex.io/documentations` | `https://api.sarex.io/documentations` | `/documentations` |
|
||||||
|
| `sarexApi` | Gateway/API Sarex (`/gateway`, `/eav`, `/cde`, `/transmittals`, `/flows`, `/issues`) | `https://stage-api.sarex.io` | `https://stage-api.sarex.io` | `https://api.preprod.sarex.io` | `https://api.sarex.io` | `/` |
|
||||||
|
| `workspaces` | Сервис рабочих областей | `https://stage-api.sarex.io/workspaces` | `https://stage-api.sarex.io/workspaces` | `https://api.preprod.sarex.io/workspaces` | `https://api.sarex.io/workspaces` | `/workspaces` |
|
||||||
|
| `workflows` | Сервис обработки документов | `https://stage-api.sarex.io/workflows` | `https://stage-api.sarex.io/workflows` | `https://api.preprod.sarex.io/workflows` | `https://api.sarex.io/workflows` | `/workflows` |
|
||||||
|
| `processes` | Сервис рабочих процессов (flows, reviews) | `https://stage-api.sarex.io/flows` | `https://stage-api.sarex.io/flows` | `https://api.preprod.sarex.io/flows` | `https://api.sarex.io/flows` | `/flows` |
|
||||||
|
| `remarks` | Сервис замечаний | `https://stage-api.sarex.io/remarks` | `https://stage-api.sarex.io/remarks` | `https://api.preprod.sarex.io/remarks` | `https://api.sarex.io/remarks` | `/remarks` |
|
||||||
|
| `files` | Сервис файлов | `https://stage-api.sarex.io/files` | `https://stage-api.sarex.io/files` | `https://api.preprod.sarex.io/files` | `https://api.sarex.io/files` | `/files` |
|
||||||
|
| `google` | Временное хранилище (GCS) | `https://storage.googleapis.com/srx-tmp` | `https://storage.googleapis.com/srx-tmp` | `https://storage.googleapis.com/srx-tmp` | `https://storage.googleapis.com/srx-tmp` | `https://storage.googleapis.com/srx-tmp` |
|
||||||
|
| `bim` | BIM-API | `https://stage-bim-api.sarex.io` | `https://stage-bim-api.sarex.io` | `https://bim-api.preprod.sarex.io` | `https://bim-api.sarex.io` | `""` |
|
||||||
|
| `zitadel` | IdP (аутентификация) | `https://idp.dev.stage.sarex.io` | `https://idp.dev.stage.sarex.io` | `https://login.preprod.sarex.io` | `https://login.sarex.io` | `""` |
|
||||||
|
|
||||||
|
> Хосты `google`, `bim` и `zitadel` заданы в карте хостов, но напрямую в реестре `endpoints` не используются — они задействованы через SDK/вьюер (`@sarex-team/sdk-js`) и механизм аутентификации. В окружении `local` сервис `sarex` проксируется на `/sarex-backend`, в `stage`/`preprod`/`prod` — на `/` (относительные пути), в `contour` все сервисы работают по относительным путям изолированного контура. Отдельного `module-hosts.ts` (карты хостов удалённых модулей) в репозитории нет.
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `sarex` — Локальный сервис данных
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getSettings` | GET | `/api/client/settings/` | Клиентские настройки (кешируется, `stateTime: 5`) |
|
||||||
|
| `getUser` | GET | `/api/core/users/{userId}/` | Пользователь по id |
|
||||||
|
| `getUsersByCompanyId` | GET | `/api/core/users/?company={companyId}&limit={limit}&offset={offset}` | Пользователи компании (пагинация) |
|
||||||
|
| `getTargets` | GET | `/api/core/targets/` | Список таргетов |
|
||||||
|
| `getCompanies` | GET | `/api/core/companies/` | Список компаний |
|
||||||
|
| `getDepartmentById` | GET | `/api/core/admin/departments?company={companyId}` | Отделы компании |
|
||||||
|
| `getUsersPositionById` | GET | `/api/core/admin/positions/?company={companyId}` | Должности компании |
|
||||||
|
| `getMrpaList` | POST | `/api/core/mrpa/list/` | Список МЧД (фильтр по пользователю/компании) |
|
||||||
|
| `getByFullUrl` | GET | `{url}` | Запрос по произвольному URL |
|
||||||
|
|
||||||
|
### `documentations` — Сервис документации
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getDisks` | GET | `/api/v1/disks` | Список дисков |
|
||||||
|
| `getDocumentTypes` | GET | `/api/v1/documents/types` | Справочник типов документов |
|
||||||
|
| `getAllPermmission` | GET | `/api/v1/permissions` | Все права доступа |
|
||||||
|
| `getDocPermission` | GET | `/api/v1/documents/{id}/permissions` | Права доступа документа |
|
||||||
|
| `postPermission` | POST | `/api/v1/documents/{id}/permissions` | Выдать права сервисному аккаунту |
|
||||||
|
| `postBundle` | POST | `/api/v1/bundles` | Создать бандл |
|
||||||
|
| `postFile` | POST | `/api/v1/bundles/{bundleId}/{fileKey}?single_upload=1` | Загрузить файл (single upload) |
|
||||||
|
| `uploadFileStart` | POST | `/api/v1/bundles/{bundleId}/{key}/upload_multipart` | Начать multipart-загрузку файла |
|
||||||
|
| `uploadFolderStart` | POST | `/api/v1/bundles/{bundleId}/{key}/upload_multipart?upload_path={folderPath}` | Начать multipart-загрузку с указанием пути |
|
||||||
|
| `uploadPart` | PUT | `/api/v1/bundles/{bundleId}/{bundleKey}?part_number={partNumber}` | Загрузить часть файла |
|
||||||
|
| `bundleComplite` | POST | `/api/v1/bundles/{bundleId}/upload_finish` | Завершить загрузку бандла |
|
||||||
|
| `bundleCompleteUpload` | POST | `/api/v1/bundles/{bundleId}/upload_finish` | Завершить загрузку бандла (дубль ключа) |
|
||||||
|
| `getFile` | GET | `/api/v1/bundles/{bundleId}/{bundleKey}` | Получить файл бандла |
|
||||||
|
| `updateComment` | PATCH | `/api/v1/bundles/{bundleId}/comment` | Обновить комментарий бандла |
|
||||||
|
| `getBundles` | GET | `/api/v1/documents/{id}/bundles` | Бандлы документа |
|
||||||
|
| `addBundle` | POST | `/api/v1/documents/{documentId}/add_bundle` | Привязать бандл к документу |
|
||||||
|
| `moveBundles` | PATCH | `/api/v1/documents/{documentId}/move_bundles` | Переместить бандлы |
|
||||||
|
| `removeBundle` | DELETE | `/api/v1/bundles/{id}` | Удалить бандл |
|
||||||
|
| `postWorkspace` | POST | `/api/v1/workspaces` | Создать рабочую область |
|
||||||
|
| `getDocumentById` | GET | `/api/v1/documents/{id}` | Документ по id |
|
||||||
|
| `getDocumentWithBundles` | GET | `/api/v1/documents/{id}?extend=bundles` | Документ с бандлами |
|
||||||
|
| `changeDocument` | PATCH | `/api/v1/documents/{documentId}` | Переименовать документ |
|
||||||
|
| `changeDocumentName` | PATCH | `/api/v1/documents/{id}` | Переименовать документ |
|
||||||
|
| `updatePath` | PATCH | `/api/v1/documents/{id}/update-path` | Сменить родителя документа |
|
||||||
|
| `updateDocumentsPaths` | PATCH | `/api/v1/documents/update-path` | Массовая смена родителя |
|
||||||
|
| `deleteDocument` | DELETE | `/api/v1/documents/{id}` | Удалить документ |
|
||||||
|
| `deleteDocuments` | DELETE | `/api/v1/documents?document_ids={ids}` | Удалить несколько документов |
|
||||||
|
| `getFolderChildrenWithActiveProcesses` | POST | `/api/v1/documents/flows` | Дети папки с активными процессами |
|
||||||
|
| `downloadFile` | GET | `/api/v1/bundles/{lastBundleId}/{key}/download` | Скачать файл (с флагами `include_original_pdf`/`include_printable_pdf`) |
|
||||||
|
| `downloadFiles` | GET | `/api/v1/download_url/documents?document_ids={documentIds}` | Получить ссылку на скачивание документов |
|
||||||
|
| `downloadAllFiles` | GET | `/api/v1/bundles/{lastBundleId}/download` | Скачать все файлы бандла |
|
||||||
|
| `downloadFolder` | GET | `/api/v1/documents/{docId}/download?depth={depth}` | Скачать папку |
|
||||||
|
| `getFoldersDownloadUrl` | GET | `/api/v1/documents/get_folders_download_url?document_ids={ids}` | Ссылка на скачивание папок |
|
||||||
|
| `conversionFile` | POST | `/api/v1/conversion` | Конвертация документа (в IFC) |
|
||||||
|
| `addMarks` | PUT | `/api/v1/bundles/{bundleId}/marks` | Добавить штампы/QR/подписи |
|
||||||
|
| `sign` | POST | `/api/v1/bundles/{bundleId}/sign` | Подписать бандл |
|
||||||
|
| `cancelQrCode` | PATCH | `/api/v1/bundles/{bundleId}/cancel_qr` | Отменить QR-код |
|
||||||
|
| `restartWorkflow` | POST | `/api/v1/bundles/{bundleId}/restart` | Перезапустить workflow бандла |
|
||||||
|
| `getPublicLink` | GET | `/api/v1/public/documents/public_link/{public_link_id}` | Получить публичную ссылку |
|
||||||
|
| `createPublicLink` | POST | `/api/v1/documents/public_link` | Создать публичную ссылку |
|
||||||
|
| `updatePublicLink` | PATCH | `/api/v1/documents/public_link/{public_link_id}` | Обновить публичную ссылку |
|
||||||
|
| `deletePublicLink` | DELETE | `/api/v1/documents/public_link/{public_link_id}` | Удалить публичную ссылку |
|
||||||
|
| `removeDoc` | DELETE | `/api/v1/documents/bin?parent_id={id}` | Переместить в корзину |
|
||||||
|
| `recoveryDocument` | PATCH | `/api/v1/documents/bin/restore?parent_id={id}` | Восстановить из корзины |
|
||||||
|
| `copyFolderStructure` | POST | `/api/v1/documents/copy_structure` | Копировать структуру папок |
|
||||||
|
| `getTemplateJSON` | GET | `/api/v1/templates/{bundleId}` | JSON-шаблон бандла |
|
||||||
|
| `uploadSingleTemplate` | POST | `/api/v1/bundles/{bundleId}/{key}/upload_single` | Загрузить файл шаблона |
|
||||||
|
| `createReport` | POST | `/api/v1/documents/create_report` | Сформировать отчёт по документам |
|
||||||
|
| `updateChangelog` | PATCH | `api/v1/changelogs/{bundleId}` | Обновить запись журнала изменений |
|
||||||
|
| `createChangelog` | POST | `api/v1/changelogs/create` | Создать запись журнала изменений |
|
||||||
|
| `getFavorites` | GET | `/api/v1/favorite_documents?company_id={companyId}` | Избранные документы |
|
||||||
|
| `createFavoriteDocument` | POST | `/api/v1/favorite_documents` | Добавить документ в избранное |
|
||||||
|
| `deleteFavoriteDocument` | DELETE | `/api/v1/favorite_documents/{documentId}` | Убрать документ из избранного |
|
||||||
|
| `getNearestNameTemplate` | GET | `/api/v1/documents/{documentId}/name_template` | Ближайший шаблон именования (без уведомления об ошибке) |
|
||||||
|
| `getDocumentNameTemplate` | GET | `/api/v1/name_templates/{documentId}` | Шаблон именования документа (без уведомления об ошибке) |
|
||||||
|
| `createNameTemplate` | POST | `/api/v1/name_templates/create` | Создать шаблон именования |
|
||||||
|
| `updateNameTemplate` | PATCH | `/api/v1/name_templates/{documentId}` | Обновить шаблон именования |
|
||||||
|
| `deleteNameTemplate` | DELETE | `/api/v1/name_templates/{documentId}` | Удалить шаблон именования |
|
||||||
|
| `createLink` | POST | `/api/v1/links` | Создать ярлык (ссылку на документ) |
|
||||||
|
| `getBundleMrpas` | GET | `/api/v1/bundles/{bundleId}/mrpas` | МЧД бандла |
|
||||||
|
|
||||||
|
### `sarexApi` — Gateway/API Sarex
|
||||||
|
|
||||||
|
Через этот сервис проходят запросы к `/gateway`, `/eav`, `/cde`, а также к сабпутям других доменов, доступным через общий шлюз: `/transmittals`, `/flows`, `/issues`.
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getDocuments` | GET | `/gateway/api/v1/disks/{id}/documents?parent_id={parentId}&child_id={childId}` | Документы диска (по родителю/потомку) |
|
||||||
|
| `getFolderChildren` | GET | `/gateway/api/v1/disks/{diskId}/documents?parent_id={documentId}` | Дети папки |
|
||||||
|
| `getSearch` | GET | `/gateway/api/v4/disks/{diskId}/documents?...` | Поиск/фильтрация документов (root_document, limit, filters, bookmark) |
|
||||||
|
| `createDocument` | POST | `/gateway/api/v1/documents` | Создать документ/папку/проект |
|
||||||
|
| `fetchDocumentPaths` | POST | `/gateway/api/v1/documents/ancestors` | Предки документов |
|
||||||
|
| `getAttributesByDocument` | GET | `/gateway/api/v1/documents/{id}/attributes` | Атрибуты документа |
|
||||||
|
| `updateAttributes` | PUT | `/gateway/api/v1/documents/{id}/attributes` | Обновить атрибуты документа |
|
||||||
|
| `addAttributes` | POST | `/gateway/eav/api/v0/entity/` | Создать сущность атрибутов (EAV) |
|
||||||
|
| `getDefaultAttributes` | GET | `/eav/api/v0/schema/?model=document&company_id={companyId}&type_identifier={docType}` | Схема атрибутов по типу |
|
||||||
|
| `getAttributes` | GET | `/eav/api/v0/attribute/?company_id={params}` | Атрибуты компании |
|
||||||
|
| `getAttributesWithParams` | GET | `/eav/api/v0/schema/?model=document&{params}` | Схема атрибутов с параметрами |
|
||||||
|
| `updateSubscription` | POST | `/gateway/api/v1/subscription/` | Создать/обновить подписку |
|
||||||
|
| `deleteSubscription` | DELETE | `/gateway/api/v1/documents/{documentId}/subscription/` | Удалить подписку |
|
||||||
|
| `getActivityLog` | GET | `/gateway/api/v1/system_log/?model_names=document&...` | Журнал активности документа |
|
||||||
|
| `fetchResourceByDocumentId` | GET | `/gateway/api/v1/resources-rpc/resource-by-document-id/{id}/` | Ресурс по id документа |
|
||||||
|
| `getUsersWithTransmittalProjectPermissions` | GET | `/gateway/api/v2/users/?limit=5000&offset=0&resource_id={projectId}&permissions={permissions}` | Пользователи с правами в проекте |
|
||||||
|
| `getRemovedDocuments` | GET | `/gateway/api/v1/documents/bin?parent_id={id}{params}` | Удалённые документы в папке |
|
||||||
|
| `getRemovedFilteredDocuments` | GET | `/gateway/api/v1/documents/bin{params}` | Удалённые документы (фильтр) |
|
||||||
|
| `getTemplates` | GET | `/gateway/api/v1/disks/{diskId}/flat_documents?type={type}` | Плоский список документов по типу |
|
||||||
|
| `getFileSize` | GET | `/gateway/api/v1/documents/size?disk_id={diskId}&document_id={documentId}` | Размер документа |
|
||||||
|
| `completeUpload` | POST | `{uploadUrl}/complete` | Завершение загрузки (по переданному URL) |
|
||||||
|
| `createTransmittal` | POST | `/transmittals/api/v1/transmittals/create` | Создать трансмиттал |
|
||||||
|
| `getTransmittalById` | GET | `/transmittals/api/v1/transmittals/{transmittalId}` | Трансмиттал по id |
|
||||||
|
| `getTransmittalsByBundleId` | POST | `/transmittals/internal/v1/transmittals/by_bundle_ids` | Трансмитталы по id бандлов |
|
||||||
|
| `getTemplateListForSelect` | GET | `/transmittals/api/v1/transmittal_templates/select?resource={resourceId}` | Список шаблонов трансмитталов для выбора |
|
||||||
|
| `getSingleTemplate` | GET | `/transmittals/api/v1/transmittal_templates/{templateId}?resource={resourceId}` | Шаблон трансмиттала по id |
|
||||||
|
| `createPrescriptionDocument` | POST | `/issues/api/prescriptions/{prescriptionId}/generate/` | Сгенерировать документ по предписанию |
|
||||||
|
| `loadReviewData` | GET | `/flows/api/v1/documents/?bundle_ids={ids}&limit=10000&full=true` | Данные согласований по бандлам (с явным access-токеном) |
|
||||||
|
| `searchAssets` | POST | `eav/api/v4/assets/search/` | Поиск активов по id |
|
||||||
|
| `getProjectAssets` | GET | `eav/api/v4/assets/?linkable_to_project={resourceId}&parentId=null` | Активы проекта |
|
||||||
|
| `getAssetsList` | GET | `eav/api/v4/assets/?tenant_id={tenantId}&linkable_to_project={resourceId}&depth=0&...` | Список активов (поиск/пагинация) |
|
||||||
|
| `getAssets` | GET | `eav/api/v4/assets/?{params}` | Активы по произвольным параметрам |
|
||||||
|
| `getAssetLevels` | GET | `eav/api/v4/assets/?tenant_id={tenantId}&parent_id={parentAssetId}&path_contains={id}` | Уровни активов |
|
||||||
|
| `getBindings` | GET | `/cde/app/v1/bundles/{bundleId}/bindings` | Привязки бандла |
|
||||||
|
| `createSession` | POST | `/cde/app/v1/s32d/sessions/` | Создать S32D-сессию |
|
||||||
|
| `getSessionList` | GET | `/cde/app/v1/s32d/sessions/` | Список S32D-сессий |
|
||||||
|
| `uploadS32DSingle` | PUT | `/cde/app/v1/s32d/sessions/{sessionId}/archive` | Загрузить архив S32D (single) |
|
||||||
|
| `s32dMultipartInit` | POST | `/cde/app/v1/s32d/sessions/{sessionId}/archive/multipart` | Начать multipart-загрузку архива S32D |
|
||||||
|
| `s32dMultipartUploadPart` | PUT | `/cde/app/v1/s32d/sessions/{sessionId}/archive/multipart/{uploadId}/parts/{partNumber}` | Загрузить часть архива S32D |
|
||||||
|
| `s32dMultipartComplete` | POST | `/cde/app/v1/s32d/sessions/{sessionId}/archive/multipart/{uploadId}/complete` | Завершить multipart-загрузку S32D |
|
||||||
|
| `startS32DProcess` | POST | `/cde/app/v1/s32d/sessions/{sessionId}/process` | Запустить обработку S32D |
|
||||||
|
|
||||||
|
### `processes` — Сервис рабочих процессов (flows)
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getProcesses` | GET | `/api/v1/flows/?{query}` | Список процессов (flows) |
|
||||||
|
| `createReview` | POST | `/api/v1/reviews/` | Создать review |
|
||||||
|
| `deleteReview` | DELETE | `/api/v1/reviews/{id}/` | Удалить review |
|
||||||
|
| `activateReview` | PATCH | `/api/v1/reviews/{id}/approve/` | Активировать/утвердить review |
|
||||||
|
| `createReviewDocuments` | POST | `/api/v1/documents/` | Добавить документы в review |
|
||||||
|
|
||||||
|
### `workflows` — Сервис обработки документов
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getWorkflow` | GET | `/api/v1/workflows/{workflowId}` | Workflow по id |
|
||||||
|
| `getWorkflows` | POST | `/api/v1/workflows/batch` | Пакетное получение workflow |
|
||||||
|
|
||||||
|
### `workspaces` — Сервис рабочих областей
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getWorkspaces` | GET | `/api/v1/workspaces/{uuid}` | Рабочая область по uuid |
|
||||||
|
|
||||||
|
### `remarks` — Сервис замечаний
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `getRemarksTotalCount` | GET | `/api/v1/total_count` | Общее число замечаний |
|
||||||
|
|
||||||
|
### `files` — Сервис файлов
|
||||||
|
|
||||||
|
| Ключ | Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `downloadBundlesMrpas` | POST | `/api/v1/bundles_mrpas/` | Скачать МЧД бандлов (ответ `blob`) |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Маппинг ошибок выполняется в `module/api/errors.ts`. Функция `fetch` (`module/api/endpoints.ts`) перехватывает `AxiosError` и вызывает:
|
||||||
|
|
||||||
|
- `resolveNetworkErrorByCode(service, code, endpoint, method, data)` — при HTTP-ответе с кодом ≥ 400 и при отмене запроса (`CanceledError`/`ERR_CANCELED`, внутренний код `-1` → «Запрос был отменен»);
|
||||||
|
- `resolveNetworkError(service, error)` — при сетевой ошибке без ответа сервера.
|
||||||
|
|
||||||
|
Базовый маппинг кодов (`httpCodeToError`): `400` — «некорректный формат запроса», `401` — «ошибка авторизации», `403` — «доступ запрещен», `404` — «ресурс не найден», `500` — «ошибка сервера». Для неизвестного кода подбирается ближайший (`4xx` → `400`, иначе → `500`). К сообщению добавляется человекочитаемое имя сервиса из `getServiceToName()` (напр. `documentations`/`sarexApi` → «Сервис документации», `sarex` → «Локальный сервис данных», `processes` → «Сервис рабочих процессов», `workflows` → «Сервис обработки документов», `remarks` → «Сервис замечаний», `workspaces` → «Сервис рабочих областей», `files` → «Сервис файлов»).
|
||||||
|
|
||||||
|
Для части кодов сообщение уточняется по эндпоинту и методу:
|
||||||
|
|
||||||
|
- `401` — набор сообщений `error401Messages` (истёкшая/невалидная сессия, завершённая сессия); по умолчанию — «Ваш токен невалиден, обновите страницу».
|
||||||
|
- `403` — тип определяется `determine403ErrorType(endpoint, method)` (напр. чтение/создание/редактирование/удаление/перемещение документа, скачивание, загрузка файла, управление доступом, изменение атрибутов, создание review/трансмиттала, доступ к диску/проекту), сообщения — `error403Messages`.
|
||||||
|
- `400` — тип определяется `determine400ErrorType(endpoint, method, data)` с анализом текста `data.message` (конфликт имени, дубликат, отсутствие/некорректность расширения, некорректный формат), сообщения — `error400Messages`.
|
||||||
|
- `409` — тип определяется `determine409ErrorType(endpoint, method, data)` (дубликаты имён документов/папок/ярлыков, конфликт при `copy_structure`), сообщения — `error409Messages`.
|
||||||
|
|
||||||
|
По умолчанию у запросов включён показ уведомления об ошибке (`showErrorNotification !== false`); отдельные эндпоинты отключают его (`getNearestNameTemplate`, `getDocumentNameTemplate`). Типы кодов ошибок описаны в `module/api/types.ts`.
|
||||||
241
apps/documentations/pdm.CONFIGURATION.md
Normal file
241
apps/documentations/pdm.CONFIGURATION.md
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# Конфигурация проекта pdm
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса **pdm** (Go). Сервис деплоится в namespace `documentations` как деплоймент `pdm` (образ `pdmv2`). Это шлюз/агрегатор поверх Postgres и множества внутренних сервисов Sarex (документации, ресурсы, ремарки, вложения, состояния, подписки, EAV, инспекции, релизы, BIM, трансмитталы и др.).
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `config/config.go` (функция `NewConfig`) через библиотеку [`cleanenv`](https://github.com/ilyakaznacheev/cleanenv) — вызовом `cleanenv.ReadEnv(cfg)`.
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- **Плоские имена переменных без общего префикса** — каждое поле помечено тегом `env:"..."` (напр. `POSTGRES_ADDRESS`, `RESOURCES_URL`). Вложенности/делимитера, как в pydantic-settings, здесь нет.
|
||||||
|
- **Обязательность** задаётся тегом `env-required:"true"` — при отсутствии такой переменной приложение не стартует (`config error`). В таблицах ниже дефолт `—` означает обязательное поле.
|
||||||
|
- **Значения по умолчанию** задаются тегом `env-default:"..."`.
|
||||||
|
- `cleanenv.ReadEnv` читает **только переменные окружения процесса** — конфиг-файла (yaml/toml) и авто-загрузки `.env` нет. Единственный файловый источник — JSON сервисного аккаунта S3 (`S3_SERVICE_ACCOUNT`).
|
||||||
|
|
||||||
|
Отдельно от env читается JSON-файл доступа к S3 — путь берётся из `S3_SERVICE_ACCOUNT`, разбор в `pkg/s3` (`NewFromConfigFile`). Формат файла (`.example.s3config.json`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "endpoint": "", "access_key_id": "", "secret_access_key": "", "use_ssl": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса. `make config` копирует `.example.env` → `.env` и `.example.s3config.json` → `.s3config.json` (только если файлов ещё нет), но приложение **не загружает `.env` автоматически** — его нужно экспортировать самому. В репозитории для этого есть `.envrc` (`use flake` + `dotenv`) под direnv |
|
||||||
|
| Локально (live-reload) | `make run-dev` → `air` (`.air.toml`), пересборка `./cmd/httpserver/main.go` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`: блок `services.api.envs` (обычные значения, ключ `_default` и переопределения по окружениям `stage`/`preprod`/`production`) и `services.api.secretEnvs` (значения из k8s-секретов через `secretKeyRef`). Используется зонтичный `universal-chart` |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна в `workflow.rules` (по ветке/тегу), общие шаблоны из `generic/common-ci` |
|
||||||
|
|
||||||
|
Точки входа (`cmd/`):
|
||||||
|
|
||||||
|
| Команда | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `make run` / `make build` | `cmd/httpserver/main.go` | HTTP API (Fiber). Читает конфиг и вызывает `internal/app/http.New(cfg).Run()` |
|
||||||
|
| — | `cmd/example/main.go`, `cmd/test/main.go` | Вспомогательные утилиты (не участвуют в деплое) |
|
||||||
|
|
||||||
|
Порядок инициализации в `internal/app/http/httpserver.go` (`App.Run`): трейсер (при `TRACER_USE=true`) → подключение к Postgres → инициализация HTTP-клиентов внешних сервисов → репозитории/usecase/сервисы → опциональный Valkey → сборка Fiber-приложения (`internal/controller/http/v1.Setup`) → `app.Listen(":8080")`.
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Все переменные читаются `config/config.go`. Дефолт `—` означает, что значение обязательно (`env-required:"true"`) и его отсутствие приводит к ошибке старта.
|
||||||
|
|
||||||
|
### App / Log
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `APP_NAME` | string | — | Имя приложения |
|
||||||
|
| `APP_VERSION` | string | — | Версия приложения |
|
||||||
|
| `LOG_LEVEL` | string | — | Уровень логирования (`pkg/logging`, напр. `DEBUG`/`INFO`) |
|
||||||
|
|
||||||
|
### Postgres (`POSTGRES`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `POSTGRES_ADDRESS` | string | — | Хост PostgreSQL |
|
||||||
|
| `POSTGRES_DB` | string | — | Имя базы данных |
|
||||||
|
| `POSTGRES_USER` | string | — | Пользователь БД |
|
||||||
|
| `POSTGRES_PASSWORD` | string | — | Пароль пользователя БД |
|
||||||
|
| `POSTGRES_PORT` | string | — | Порт PostgreSQL |
|
||||||
|
| `POSTGRES_POOL_SIZE` | int32 | — | Размер пула соединений (pgxpool) |
|
||||||
|
| `ENABLE_OBSERVABILITY` | bool | `false` | Инструментирование пула Postgres трейсингом (`otelpgx`) |
|
||||||
|
|
||||||
|
> DSN собирается в `Config.GetPostgresConnectionUrl()` как `postgres://user:password@address:port/db` — **без параметра `sslmode`**. Отдельный флаг `ENABLE_SSL` из Helm кодом не читается (см. «Замечания»).
|
||||||
|
|
||||||
|
### HTTP (`HTTP`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `HTTP_PORT` | string | — | Порт HTTP. **Обязателен по тегу, но фактически не используется** — сервер слушает `:8080` (хардкод в `httpserver.go`) |
|
||||||
|
| `PUBLIC_KEY` | string (PEM) | `""` | Публичный ключ (PKIX) для проверки JWT. Формально необязателен, но при пустом/некорректном значении приложение падает (`panic` в `v1.Setup`) |
|
||||||
|
| `HTTP_BODY_LIMIT` | int | `268435456` (256 MB) | Максимальный размер тела запроса, байт |
|
||||||
|
| `HTTP_READ_BUFFER_SIZE` | int | `98304` (96 KB) | Размер буфера чтения запроса, байт |
|
||||||
|
|
||||||
|
### Auth и хосты внешних сервисов
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DJANGO_BASIC_AUTH` | string | — | Basic-токен для авторизации в бэкенде Sarex/Django (клиенты `targets`, `users`) |
|
||||||
|
| `DJANGO_HOST` | string | — | Базовый URL Django/бэкенда. Используется сразу двумя секциями — `USERS.UserHost` и `SA.SAHost` (accounts/companies/django-клиенты) |
|
||||||
|
| `NOTES_URL` | string | — | Сервис заметок (`notes`) |
|
||||||
|
| `FLOWS_URL` | string | — | Сервис процессов (`flows`) |
|
||||||
|
| `RESOURCES_URL` | string | — | Сервис ресурсов/IAM (`resources`) |
|
||||||
|
| `REMARKS_URL` | string | — | Сервис замечаний (`remarks`) |
|
||||||
|
| `ATTACHMENTS_URL` | string | — | Сервис вложений (`attachments`) |
|
||||||
|
| `STATES_URL` | string | — | Сервис состояний/рабочих областей (`workspaces`) |
|
||||||
|
| `SUBSCRIPTIONS_URL` | string | — | Сервис подписок (`subscriptions`) |
|
||||||
|
| `EAV_URL` | string | — | Сервис атрибутов EAV |
|
||||||
|
| `INSPECTIONS_URL` | string | — | Сервис инспекций |
|
||||||
|
| `SYSTEM_LOG_URL` | string | — | Сервис системного лога |
|
||||||
|
| `TARGET_URL` | string | — | Сервис таргетов |
|
||||||
|
| `DOCUMENTATION_URL` | string | — | Сервис документаций (documentation-api-v2) |
|
||||||
|
| `BIM_V2_HOST` | string | — | BIM core API v2 |
|
||||||
|
| `NOTES_URL` | string | — | (см. выше) |
|
||||||
|
| `DRAWINGS_INTERNAL_URL` | string | — | Внутренний URL сервиса чертежей |
|
||||||
|
| `RELEASES_URL` | string | — | URL GitLab для получения релизов |
|
||||||
|
| `RELEASES_TOKEN` | string | — | Токен доступа к GitLab (`RELEASES_URL`) |
|
||||||
|
|
||||||
|
### Ресурсы и фильтр разрешений (`RESOURCES`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_PERMISSIONS_FILTER` | bool | `false` | Включить фильтрацию по разрешениям на уровне сервиса документов |
|
||||||
|
| `PERMISSIONS_FILTER_COMPANIES` | string (JSON-массив) | `[133, 256, 247, 219, 248, 194, 242, 260, 252, 255, 239, 125, 116, 92, 311, 170]` | Список ID компаний, к которым применяется фильтр. Парсится `json.Unmarshal` в `[]uint64` |
|
||||||
|
|
||||||
|
### Thumbnails (`ATTACHMENTS`, `STATES`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `WIDTH_THUMB_ATTACHMENTS` | int | `100` | Ширина превью вложений |
|
||||||
|
| `HEIGHT_THUMB_ATTACHMENTS` | int | `100` | Высота превью вложений |
|
||||||
|
| `WIDTH_THUMB_STATES` | int | `100` | Ширина превью состояний |
|
||||||
|
| `HEIGHT_THUMB_STATES` | int | `100` | Высота превью состояний |
|
||||||
|
|
||||||
|
### Subscriptions / System log — доп. поля
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USE_SUBSCRIPTIONS` | bool | `true` | Включить интеграцию с подписками в сервисе документов |
|
||||||
|
| `API_HOST_PREFIX` | string | `""` | Префикс хоста API (напр. `/gateway`), используется сервисом системного лога |
|
||||||
|
|
||||||
|
### S3 (`S3`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `S3_SERVICE_ACCOUNT` | string | — | Путь к JSON-файлу с доступом к S3 (`endpoint`, `access_key_id`, `secret_access_key`, `use_ssl`). Разбирается в `pkg/s3` |
|
||||||
|
|
||||||
|
### Transmittals (`Transmittals`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `TRANSMITTALS_ENABLE` | bool | `true` | Включить клиент трансмитталов; при `false` используется stub-реализация |
|
||||||
|
| `TRANSMITTALS_BASE_URL` | string | — | Базовый URL сервиса трансмитталов. Обязателен даже при `TRANSMITTALS_ENABLE=false` |
|
||||||
|
|
||||||
|
### Observability / Tracer
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `OBSERVABILITY_COLLECTOR_ENDPOINT` | string | `""` | Эндпоинт OTLP-коллектора (метрики/наблюдаемость) |
|
||||||
|
| `TRACER_USE` | bool | `false` | Включить трейсинг OpenTelemetry (`golang-fiber-otel-tools`) |
|
||||||
|
| `TRACER_HOST` | string | `localhost:4317` | Адрес OTLP-коллектора трейсов |
|
||||||
|
| `TRACER_USE_INSECURE` | bool | `true` | Небезопасное (без TLS) подключение к коллектору |
|
||||||
|
| `SERVICE_NAME` | string | `Pdm` | Имя сервиса в трейсах |
|
||||||
|
| `TRACER_LOGGER_NAME` | string | `tracer_logger` | Имя логгера OTel |
|
||||||
|
|
||||||
|
### Valkey (`VALKEY`) — кэш пользователей
|
||||||
|
|
||||||
|
Подключение опционально: если `VALKEY_ADDR` пуст — клиент не создаётся (кэш пользователей отключён). Ошибки подключения/пинга не фатальны (лог `Warn`).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `VALKEY_ADDR` | string | `""` | Адрес Valkey (при пустом — кэш выключен) |
|
||||||
|
| `VALKEY_LOGIN` | string | `""` | Логин |
|
||||||
|
| `VALKEY_HOST` | string | `""` | Хост |
|
||||||
|
| `VALKEY_PASSWORD` | string | `""` | Пароль |
|
||||||
|
| `VALKEY_DB` | int | `0` | Номер БД |
|
||||||
|
| `VALKEY_SSL` | bool | `false` | Использовать TLS |
|
||||||
|
| `VALKEY_SSL_CA_CERTS` | string | `""` | Путь к CA-сертификату для TLS |
|
||||||
|
|
||||||
|
## Переменные инфраструктуры и сборки
|
||||||
|
|
||||||
|
Не читаются кодом приложения (`config/config.go`), но участвуют в сборке/деплое:
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `GITLAB_CREDENTIALS` | `Dockerfile` (build-arg), `.gitlab-ci.yml` (`BUILD_ARGS`) | Учётные данные для доступа к приватным Go-модулям `gitlab.sarex.io` при сборке |
|
||||||
|
| `SERVICE_NAME` (CI) | `.gitlab-ci.yml` | `pdmv2` — имя сервиса в пайплайне (не путать с `SERVICE_NAME` трейсера) |
|
||||||
|
| `DOCKERFILE_PATH`, `CI_TRIGGER_SOURCE` | `.gitlab-ci.yml` | Путь к Dockerfile, источник триггера |
|
||||||
|
| `ENABLE_LINTER`, `ENABLE_BUILD_CHART`, `ENABLE_BUILD_IMAGE`, `ENABLE_STATE_UPDATE`, `ENABLE_DEPLOY` | `.gitlab-ci.yml` | Флаги стадий пайплайна |
|
||||||
|
| `STAND`, `NAMESPACE`, `RELEASE_NAME`, `CHART_NAME`, `CHART_VERSION`, `K8S_HUSTLER_BRANCH`, `HELM_SET_ARGS`, `IMAGE_NAME` | `.gitlab-ci.yml` | Параметры окружения/деплоя Helm |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Обычные значения задаются в `services.api.envs` (ключ `_default` + переопределения по `stage`/`preprod`/`production`) и содержат переменные приложения, описанные выше, различаясь адресами БД/сервисов, `LOG_LEVEL`, доменами и т.п.
|
||||||
|
|
||||||
|
Значения из секретов (`services.api.secretEnvs`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secretName`, prod/по умолчанию) | Ключ (`secretKey`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `POSTGRES_DB` | `documentations-postgresql-secret` (preprod: `ya-pg-secret`) | `database` |
|
||||||
|
| `POSTGRES_PORT` | `documentations-postgresql-secret` | `port` |
|
||||||
|
| `POSTGRES_ADDRESS` | `documentations-postgresql-secret` | `host` |
|
||||||
|
| `POSTGRES_USER` | `documentations-postgresql-secret` | `username` |
|
||||||
|
| `POSTGRES_PASSWORD` | `documentations-postgresql-secret` | `password` |
|
||||||
|
| `YC-PG-CERTIFICATE` | `documentations-postgresql-secret` (preprod: `yc-pg-certificate`) | `ca.crt` |
|
||||||
|
| `DJANGO_BASIC_AUTH` | `django-auth` | `key` |
|
||||||
|
| `PUBLIC_KEY` | `public-key` | `key` |
|
||||||
|
| `RELEASES_TOKEN` | `releases-token` | `key` |
|
||||||
|
| `VALKEY_ADDR` | `valkey-secret` | `url` |
|
||||||
|
| `VALKEY_LOGIN` | `valkey-secret` | `login` |
|
||||||
|
| `VALKEY_PASSWORD` | `valkey-secret` | `password` |
|
||||||
|
| `VALKEY_HOST` | `valkey-secret` | `host` |
|
||||||
|
| `VALKEY_PORT` | `valkey-secret` | `port` |
|
||||||
|
| `VALKEY_CA_CERTS` | `valkey-secret` | `cert` |
|
||||||
|
|
||||||
|
Помимо env, чарт монтирует секрет `documentations-yc-s3` как том в `/etc/sarex/yc-s3-storage` (readOnly). Именно на файл `/etc/sarex/yc-s3-storage/yc-s3-service-account.json` указывает `S3_SERVICE_ACCOUNT` в prod-конфигурации.
|
||||||
|
|
||||||
|
Прочие значения чарта (не переменные приложения): `deployment.*` (имя `pdm-api`, реплики `stage=3`/`preprod=2`/`production=8`, ресурсы `cpu=1`, `memory=2Gi`), `image.name` (`cr.yandex/.../pdm_v2`), `service.*` (порт `8080`), `imagePullSecrets` (`dockerhub`), `probes.*` (startup/liveness/readiness по `/internal/healthz/*` на порту `8080`).
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml` ref `apps-business`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace | `universal-chart.global.env` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `documentations` | `stage` |
|
||||||
|
| ветка `master` | `preprod` | `documentations-preprod` | `preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `prod` | `documentations-prod` | `production` |
|
||||||
|
| merge request | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||||||
|
|
||||||
|
Общие переменные: `SERVICE_NAME=pdmv2`, `RELEASE_NAME=pdmv2`, `CHART_NAME=pdmv2`, `CHART_VERSION=0.0.1-<env>`, `DOCKERFILE_PATH=Dockerfile`.
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- **`HTTP_PORT` фактически игнорируется.** Поле обязательно (`env-required`), но сервер жёстко слушает `:8080` (`app.Listen(":8080")` в `httpserver.go`). Реальный порт задаётся только этим хардкодом; в Helm `HTTP_PORT` и `service.port` совпадают со `8080`, поэтому расхождение незаметно.
|
||||||
|
- **`PUBLIC_KEY` де-факто обязателен.** По тегам он необязателен (`env:"PUBLIC_KEY"`), но `v1.Setup` при пустом/битом PEM вызывает `panic` (`failed to parse PEM block...`). Для локального запуска нужен валидный публичный ключ.
|
||||||
|
- **SSL к Postgres в коде не настраивается.** DSN формируется без `sslmode` (`GetPostgresConnectionUrl`). Helm-переменные `ENABLE_SSL` и секрет `YC-PG-CERTIFICATE` кодом **не читаются** — подключение к БД идёт без TLS-параметров на уровне DSN.
|
||||||
|
- **Множество Helm-переменных не читается приложением.** В `services.api.envs`/`secretEnvs` присутствуют переменные, отсутствующие в `config/config.go`, — вероятно, унаследованы от `documentation-api`: `API_ADDRESS`, `API_ADDRESS_FILE`, `ENABLE_SSL`, `ENABLE_S3`, `FILE_URL_EXTERNAL`, `WORKFLOW_URL`, `WORKSPACE_URL`, `BIM_API_URL`, `BIM_API_V2_URL`, `BIM_API_URL_EXTERNAL`, `WORKSPACE_BUNDLE_VERSION`, `WORKFLOW_IMAGES_VERSION`/`WORKFLOWS_IMAGES_VERSION`, `NAMESPACE`, `DJANGO_ORIGINATOR`, `USE_EXPERIMENTAL`, `READ_WRITE_TIMEOUT_FILE_STREAM`, `CACHE_DEFAULT_EXPIRATION`, `CACHE_CLEANUP_INTERVAL`, `USE_CACHE_IN_FILE_STREAMER`, `SENTRY_DSN`, `SENTRY_DEBUG`, `ENVIRONMENT`, `YC-PG-CERTIFICATE`. Они не влияют на работу pdm.
|
||||||
|
- **Несовпадение имён Valkey.** Код ждёт `VALKEY_SSL_CA_CERTS` (`config.go`), а Helm-секрет прокидывает `VALKEY_CA_CERTS`; также Helm задаёт `VALKEY_PORT`, который код не читает (адрес берётся целиком из `VALKEY_ADDR`). В результате CA-сертификат и порт из секрета до приложения не доходят.
|
||||||
|
- **Секции `USERS`/`SA` используют один и тот же env `DJANGO_HOST`.** Оба поля (`UserHost`, `SAHost`) читают одну переменную.
|
||||||
|
- **`config.env` в репозитории содержит реальные учётные данные** (пароль Postgres, `DJANGO_BASIC_AUTH`, токены) — это конфигурация для отладки, не шаблон. Для примеров использовать `.example.env`; `config.env` не должен попадать в окружения и подлежит ротации секретов.
|
||||||
|
- **`ENABLE_SQL_QUERY` из `config.env` кодом не читается** — в `config/config.go` такого поля нет.
|
||||||
|
- **`cleanenv.ReadEnv` не загружает `.env` автоматически.** `make config` лишь создаёт файлы-шаблоны (`.env`, `.s3config.json`) при их отсутствии; переменные нужно экспортировать вручную (например, через `direnv`/`.envrc`).
|
||||||
|
- **v0-роутер (echo) не подключён.** В `cmd/httpserver/main.go` используется только `internal/controller/http/v1.Setup` (Fiber). Пакет `internal/controller/http/v0` (на `labstack/echo`) в рантайме не задействован.
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Приложение поднимается через `make run` (или `make run-dev` с `air`). Минимально необходимо задать (обязательные поля `config/config.go`):
|
||||||
|
|
||||||
|
- `APP_NAME`, `APP_VERSION`, `LOG_LEVEL`;
|
||||||
|
- `POSTGRES_ADDRESS`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_PORT`, `POSTGRES_POOL_SIZE`;
|
||||||
|
- `HTTP_PORT` (любой — фактически используется `:8080`), а также **валидный** `PUBLIC_KEY` (иначе `panic`);
|
||||||
|
- `DJANGO_BASIC_AUTH`, `DJANGO_HOST`;
|
||||||
|
- хосты внешних сервисов: `NOTES_URL`, `FLOWS_URL`, `RESOURCES_URL`, `REMARKS_URL`, `ATTACHMENTS_URL`, `STATES_URL`, `SUBSCRIPTIONS_URL`, `EAV_URL`, `INSPECTIONS_URL`, `SYSTEM_LOG_URL`, `TARGET_URL`, `DOCUMENTATION_URL`, `BIM_V2_HOST`, `DRAWINGS_INTERNAL_URL`;
|
||||||
|
- `RELEASES_URL`, `RELEASES_TOKEN`;
|
||||||
|
- `S3_SERVICE_ACCOUNT` (путь к `.s3config.json`) и заполненный сам JSON-файл;
|
||||||
|
- `TRANSMITTALS_BASE_URL` (обязателен даже при выключенных трансмитталах).
|
||||||
|
|
||||||
|
Необязательные (есть дефолты): `ENABLE_OBSERVABILITY`, `HTTP_BODY_LIMIT`, `HTTP_READ_BUFFER_SIZE`, `ENABLE_PERMISSIONS_FILTER`, `PERMISSIONS_FILTER_COMPANIES`, `USE_SUBSCRIPTIONS`, `WIDTH_THUMB_*`/`HEIGHT_THUMB_*`, `API_HOST_PREFIX`, `TRANSMITTALS_ENABLE`, `TRACER_*`, `SERVICE_NAME`, `VALKEY_*`, `OBSERVABILITY_COLLECTOR_ENDPOINT`.
|
||||||
|
|
||||||
|
Готовые значения-примеры приведены в `pdm.env.example` (на основе `.example.env` репозитория).
|
||||||
116
apps/documentations/pdm.env.example
Normal file
116
apps/documentations/pdm.env.example
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Пример переменных окружения сервиса pdm (документируемый образ pdmv2).
|
||||||
|
# Основан на .example.env репозитория; переменные читаются config/config.go
|
||||||
|
# библиотекой cleanenv (cleanenv.ReadEnv). Значения-заглушки, замените своими.
|
||||||
|
# Файл автоматически НЕ загружается приложением — переменные нужно экспортировать
|
||||||
|
# в окружение процесса (напр. через direnv/.envrc или `set -a && . ./.env`).
|
||||||
|
|
||||||
|
# App
|
||||||
|
APP_NAME=pdm
|
||||||
|
APP_VERSION=0.1.0
|
||||||
|
|
||||||
|
# Logger
|
||||||
|
LOG_LEVEL=DEBUG
|
||||||
|
|
||||||
|
# Postgres
|
||||||
|
POSTGRES_ADDRESS=127.0.0.1
|
||||||
|
POSTGRES_DB=documentations
|
||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=password
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
POSTGRES_POOL_SIZE=10
|
||||||
|
ENABLE_OBSERVABILITY=false
|
||||||
|
|
||||||
|
# Http
|
||||||
|
# ВНИМАНИЕ: HTTP_PORT читается как обязательный, но сервер всё равно слушает :8080 (хардкод).
|
||||||
|
HTTP_PORT=8001
|
||||||
|
# PUBLIC_KEY — публичный ключ в формате PEM (PKIX) для проверки JWT.
|
||||||
|
# Формально не обязателен по тегам, но при пустом/некорректном значении приложение падает (panic).
|
||||||
|
PUBLIC_KEY=
|
||||||
|
HTTP_BODY_LIMIT=268435456
|
||||||
|
HTTP_READ_BUFFER_SIZE=98304
|
||||||
|
|
||||||
|
# Auth (Basic-токен для походов в Django/бэкенд Sarex)
|
||||||
|
DJANGO_BASIC_AUTH=
|
||||||
|
|
||||||
|
# Users / service accounts (один и тот же хост используется как DJANGO_HOST)
|
||||||
|
DJANGO_HOST=https://stage.sarex.io
|
||||||
|
|
||||||
|
# Notes
|
||||||
|
NOTES_URL=https://stage-api.sarex.io/notes
|
||||||
|
|
||||||
|
# Flows
|
||||||
|
FLOWS_URL=https://stage-api.sarex.io/flows
|
||||||
|
|
||||||
|
# Resources
|
||||||
|
RESOURCES_URL=http://localhost:9000
|
||||||
|
ENABLE_PERMISSIONS_FILTER=false
|
||||||
|
PERMISSIONS_FILTER_COMPANIES=
|
||||||
|
|
||||||
|
# Remarks
|
||||||
|
REMARKS_URL=https://stage-api.sarex.io/remarks
|
||||||
|
|
||||||
|
# Attachments
|
||||||
|
ATTACHMENTS_URL=http://localhost:8000
|
||||||
|
WIDTH_THUMB_ATTACHMENTS=100
|
||||||
|
HEIGHT_THUMB_ATTACHMENTS=100
|
||||||
|
|
||||||
|
# States (workspaces)
|
||||||
|
STATES_URL=https://stage-api.sarex.io/workspaces
|
||||||
|
WIDTH_THUMB_STATES=100
|
||||||
|
HEIGHT_THUMB_STATES=100
|
||||||
|
|
||||||
|
# Subscriptions
|
||||||
|
USE_SUBSCRIPTIONS=true
|
||||||
|
SUBSCRIPTIONS_URL=https://stage-api.sarex.io/subscriptions
|
||||||
|
|
||||||
|
# Eav
|
||||||
|
EAV_URL=http://stage-api.sarex.io/eav
|
||||||
|
|
||||||
|
# Inspections
|
||||||
|
INSPECTIONS_URL=https://stage-api.sarex.io/inspections
|
||||||
|
|
||||||
|
# System log
|
||||||
|
SYSTEM_LOG_URL=http://localhost:8888
|
||||||
|
API_HOST_PREFIX=/gateway
|
||||||
|
|
||||||
|
# Target
|
||||||
|
TARGET_URL=https://stage.sarex.io
|
||||||
|
|
||||||
|
# Documentation api
|
||||||
|
DOCUMENTATION_URL=http://localhost:6666/
|
||||||
|
|
||||||
|
# Bim v2
|
||||||
|
BIM_V2_HOST=http://localhost:8888/
|
||||||
|
|
||||||
|
# Observability (OTLP-коллектор)
|
||||||
|
OBSERVABILITY_COLLECTOR_ENDPOINT=
|
||||||
|
|
||||||
|
# Releases (GitLab)
|
||||||
|
RELEASES_URL=https://gitlab.com
|
||||||
|
RELEASES_TOKEN=
|
||||||
|
|
||||||
|
# Drawings
|
||||||
|
DRAWINGS_INTERNAL_URL=http://localhost:6666
|
||||||
|
|
||||||
|
# S3 (путь к JSON-файлу с сервисным аккаунтом, см. .example.s3config.json)
|
||||||
|
S3_SERVICE_ACCOUNT=.s3config.json
|
||||||
|
|
||||||
|
# Transmittals
|
||||||
|
TRANSMITTALS_ENABLE=true
|
||||||
|
TRANSMITTALS_BASE_URL=http://transmittal-service.transmittal-api-stage
|
||||||
|
|
||||||
|
# Tracer (OpenTelemetry)
|
||||||
|
TRACER_USE=false
|
||||||
|
TRACER_HOST=localhost:4317
|
||||||
|
TRACER_USE_INSECURE=true
|
||||||
|
SERVICE_NAME=Pdm
|
||||||
|
TRACER_LOGGER_NAME=tracer_logger
|
||||||
|
|
||||||
|
# Valkey (кэш пользователей, опционально; при пустом VALKEY_ADDR не подключается)
|
||||||
|
VALKEY_ADDR=
|
||||||
|
VALKEY_LOGIN=
|
||||||
|
VALKEY_HOST=
|
||||||
|
VALKEY_PASSWORD=
|
||||||
|
VALKEY_DB=0
|
||||||
|
VALKEY_SSL=false
|
||||||
|
VALKEY_SSL_CA_CERTS=
|
||||||
918
apps/documentations/pdm.openapi.yaml
Normal file
918
apps/documentations/pdm.openapi.yaml
Normal file
@ -0,0 +1,918 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
|
||||||
|
info:
|
||||||
|
title: PDM API
|
||||||
|
version: "0.1.0"
|
||||||
|
description: |
|
||||||
|
REST API сервиса **pdm** (`pdm/pdm`, образ `pdmv2`) — шлюз/агрегатор над
|
||||||
|
Postgres и множеством внутренних сервисов Sarex: документации, ресурсы и
|
||||||
|
разрешения, замечания, вложения, состояния/рабочие области, подписки,
|
||||||
|
атрибуты (EAV), инспекции, релизы, заметки, чертежи, BIM, трансмитталы,
|
||||||
|
системный лог.
|
||||||
|
|
||||||
|
Сервис написан на Go (**Fiber v2**). Приложение собирается в
|
||||||
|
`internal/controller/http/v1.Setup` (`internal/controller/http/v1/router.go`),
|
||||||
|
точка входа — `cmd/httpserver/main.go`. Роутинг делится на группы:
|
||||||
|
|
||||||
|
- публичный API — префикс `/api` с версиями `v1`/`v2`/`v3`/`v4`
|
||||||
|
(`group.Group("v1")` и т.д.);
|
||||||
|
- внутренний API — префикс `/internal` (healthcheck, pprof, служебные
|
||||||
|
ручки), без аутентификации на уровне приложения.
|
||||||
|
|
||||||
|
Готовой спецификации (swagger) в репозитории нет — данный документ
|
||||||
|
восстановлен из роутеров `internal/controller/http/**/router.go`.
|
||||||
|
Сервер слушает порт `8080` (хардкод в `internal/app/http/httpserver.go`).
|
||||||
|
|
||||||
|
### Аутентификация
|
||||||
|
Аутентификация применяется middleware `pkg/httpserver/middleware/auth.go` ко
|
||||||
|
всем путям с префиксом `/api`. Токен передаётся заголовком
|
||||||
|
`Authorization: Bearer <jwt>`. Поддерживаются два режима:
|
||||||
|
|
||||||
|
1. **sarex-backend** (по умолчанию) — если заголовка `Identity` нет, подпись
|
||||||
|
основного JWT проверяется публичным ключом из `PUBLIC_KEY` (PKIX). Из
|
||||||
|
claims извлекаются `user_id`, `is_superuser`, `company_ids`,
|
||||||
|
`service_accounts`, `permissions` и т.д.
|
||||||
|
2. **Zitadel** — если передан дополнительный заголовок
|
||||||
|
`Identity: Bearer <jwt>`, полезная нагрузка берётся из этого токена
|
||||||
|
(`urn:zitadel:iam:user:metadata`); основной токен кладётся как
|
||||||
|
`access_token`. Подпись identity-токена приложением не проверяется
|
||||||
|
(`ParseUnverified`) — доверие обеспечивается сетевым слоем.
|
||||||
|
|
||||||
|
При отсутствии заголовка `Authorization`, неверной схеме (не `Bearer`) или
|
||||||
|
ошибке разбора токена возвращается **401 Unauthorized** (пустое тело).
|
||||||
|
Пути `/internal/*` аутентификации на уровне приложения не требуют.
|
||||||
|
|
||||||
|
### Пагинация
|
||||||
|
Списочные эндпоинты используют пагинацию через query-параметры `limit` и
|
||||||
|
`offset` (напр. `internal/controller/http/v1/document/downloaded.go`,
|
||||||
|
`flat.go`). Единого конверта ответа нет — формат зависит от эндпоинта.
|
||||||
|
|
||||||
|
### Обработка ошибок
|
||||||
|
Ошибки бизнес-слоя оборачиваются в `AppError`
|
||||||
|
(`internal/app_errors/errors.go`) и сериализуются как JSON
|
||||||
|
`{ "message": "...", "error_code": "GW-XXXX" }`. HTTP-статус выбирается по
|
||||||
|
`error_code` в `internal/app_errors/middleware.go`:
|
||||||
|
|
||||||
|
| error_code | Статус | Значение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `GW-0001` | 404 | Ресурс не найден |
|
||||||
|
| `GW-0002` | 401 | Не аутентифицирован |
|
||||||
|
| `GW-0003` | 403 | Нет доступа |
|
||||||
|
| `GW-0004` | 400 | Некорректный запрос |
|
||||||
|
| `GW-0014` | 400 | Ошибка валидации состояния |
|
||||||
|
| `GW-0000` | 500 | Системная ошибка |
|
||||||
|
|
||||||
|
Не все роутеры используют обёртку `middleware.ErrorHandler` — часть
|
||||||
|
обработчиков возвращает ошибки/статусы напрямую, поэтому формат ответа об
|
||||||
|
ошибке может отличаться от `AppError`.
|
||||||
|
|
||||||
|
### Замечания (расхождения кода)
|
||||||
|
- Пути с сегментом `*` (напр. `/api/v1/disks/*/documents`,
|
||||||
|
`/api/v1/documents/*/attributes`, `/api/v1/targets/*/remarks/*`) — это
|
||||||
|
«жадные» wildcard-сегменты Fiber, захватывающие путь документа/цели
|
||||||
|
целиком (включая `/`). В спецификации они представлены параметром пути.
|
||||||
|
- Часть эндпоинтов завершается слэшем (`/`), часть — нет; поведение
|
||||||
|
определяется определениями групп во Fiber.
|
||||||
|
- v0-роутер (`internal/controller/http/v0`, на `labstack/echo`) в рантайме
|
||||||
|
не подключён.
|
||||||
|
|
||||||
|
contact:
|
||||||
|
name: pdm
|
||||||
|
url: https://gitlab.com/sarex-team/pdm
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: http://pdm-api.documentations:8080
|
||||||
|
description: Stage (внутренний адрес в кластере, namespace documentations)
|
||||||
|
- url: http://pdm-api.documentations-preprod:8080
|
||||||
|
description: Preprod (внутренний адрес в кластере)
|
||||||
|
- url: http://pdm-api.documentations-prod:8080
|
||||||
|
description: Production (внутренний адрес в кластере)
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: documents
|
||||||
|
description: Документы, диски, проекты, атрибуты (v1/v2/v3/v4)
|
||||||
|
- name: resources
|
||||||
|
description: Ресурсы и разрешения (v1/v2)
|
||||||
|
- name: remarks
|
||||||
|
description: Замечания по таргетам
|
||||||
|
- name: attachments
|
||||||
|
description: Вложения
|
||||||
|
- name: states
|
||||||
|
description: Состояния рабочих областей
|
||||||
|
- name: subscriptions
|
||||||
|
description: Подписки
|
||||||
|
- name: system_log
|
||||||
|
description: Системный лог
|
||||||
|
- name: targets
|
||||||
|
description: Таргеты
|
||||||
|
- name: inspections
|
||||||
|
description: Инспекции
|
||||||
|
- name: users
|
||||||
|
description: Пользователи
|
||||||
|
- name: eav
|
||||||
|
description: Атрибуты (EAV)
|
||||||
|
- name: releases
|
||||||
|
description: Релизы
|
||||||
|
- name: notes
|
||||||
|
description: Заметки
|
||||||
|
- name: drawings
|
||||||
|
description: Чертежи (сечения и экспорты)
|
||||||
|
- name: internal
|
||||||
|
description: Служебные эндпоинты (без аутентификации)
|
||||||
|
|
||||||
|
paths:
|
||||||
|
# ---------------- v1: documents ----------------
|
||||||
|
/api/v1/disks/{diskPath}/documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Список документов по диску (v1)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DiskPath'
|
||||||
|
- $ref: '#/components/parameters/Limit'
|
||||||
|
- $ref: '#/components/parameters/Offset'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/disks/{diskPath}/downloaded_documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Список скачанных документов диска
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DiskPath'
|
||||||
|
- $ref: '#/components/parameters/Limit'
|
||||||
|
- $ref: '#/components/parameters/Offset'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/disks/{diskPath}/flat_documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Плоский список документов диска
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DiskPath'
|
||||||
|
- $ref: '#/components/parameters/Limit'
|
||||||
|
- $ref: '#/components/parameters/Offset'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/disks/{diskId}/thumbnails:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Превью документов диска
|
||||||
|
parameters:
|
||||||
|
- name: diskId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/projects/:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Получить проект по ID документа
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Создать документ
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'400': { $ref: '#/components/responses/BadRequest' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/{documentPath}/attributes:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Атрибуты документа
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DocumentPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
put:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Создать/обновить атрибуты документа
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DocumentPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'400': { $ref: '#/components/responses/BadRequest' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/{documentPath}/subscription/:
|
||||||
|
delete:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Удалить подписку по ID документа
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DocumentPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/bin:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Список удалённых документов (корзина)
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/approving_users:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Согласующие пользователи
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/bundle_versions:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Версии бандлов документов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/ancestors:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Предки документов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/size:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Размер папки
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/related_documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Связанные документы
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Создать связи документов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/documents/related_documents/bulk_delete:
|
||||||
|
post:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Массовое удаление связей документов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: remarks ----------------
|
||||||
|
/api/v1/targets/{targetPath}/remarks:
|
||||||
|
post:
|
||||||
|
tags: [remarks]
|
||||||
|
summary: Создать замечание для таргета
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TargetPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/targets/{targetPath}/remarks/{remarkPath}:
|
||||||
|
get:
|
||||||
|
tags: [remarks]
|
||||||
|
summary: Получить замечание
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TargetPath'
|
||||||
|
- $ref: '#/components/parameters/RemarkPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
patch:
|
||||||
|
tags: [remarks]
|
||||||
|
summary: Обновить замечание
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TargetPath'
|
||||||
|
- $ref: '#/components/parameters/RemarkPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: resources ----------------
|
||||||
|
/api/v1/resources/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Список ресурсов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resources/users-with-resources/:
|
||||||
|
post:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Пользователи с ресурсами
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resources/permissions-bulk/:
|
||||||
|
patch:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Массовое обновление разрешений
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resources/{uuid}/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Ресурс по UUID
|
||||||
|
parameters:
|
||||||
|
- name: uuid
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string, format: uuid }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resource-permissions/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Разрешения на ресурсы
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
post:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Массовое создание разрешений на ресурс
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resource-permissions/{id}/:
|
||||||
|
delete:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Удалить разрешение на ресурс
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/bulk_delete/resource-permissions/:
|
||||||
|
post:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Массовое удаление разрешений на ресурс
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/company-resource-permissions/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Разрешения компаний на ресурсы
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
post:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Создать разрешение компании на ресурс
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/company-resource-permissions/{id}/:
|
||||||
|
delete:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Удалить разрешение компании на ресурс
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resources-rpc/resource-by-document-id/{id}/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Ресурс по ID документа
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/resources-rpc/parent-document-by-resource-id/{uuid}/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Родительская папка проекта по UUID ресурса
|
||||||
|
parameters:
|
||||||
|
- name: uuid
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string, format: uuid }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: attachments ----------------
|
||||||
|
/api/v1/attachments:
|
||||||
|
post:
|
||||||
|
tags: [attachments]
|
||||||
|
summary: Создать вложение
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [attachments]
|
||||||
|
summary: Список вложений
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/attachments/{id}:
|
||||||
|
get:
|
||||||
|
tags: [attachments]
|
||||||
|
summary: Вложение по ID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
delete:
|
||||||
|
tags: [attachments]
|
||||||
|
summary: Удалить вложение
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: states (workspace) ----------------
|
||||||
|
/api/v1/workspace/{ws_id}:
|
||||||
|
post:
|
||||||
|
tags: [states]
|
||||||
|
summary: Создать состояние с вложением
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/WsId'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [states]
|
||||||
|
summary: Состояния с вложениями
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/WsId'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/workspace/{ws_id}/dynamic_states:
|
||||||
|
post:
|
||||||
|
tags: [states]
|
||||||
|
summary: Создать динамические состояния
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/WsId'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [states]
|
||||||
|
summary: Динамические состояния
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/WsId'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/workspace/states/{id}:
|
||||||
|
get:
|
||||||
|
tags: [states]
|
||||||
|
summary: Состояние по ID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/workspace/dynamic_states/{id}:
|
||||||
|
get:
|
||||||
|
tags: [states]
|
||||||
|
summary: Динамическое состояние по ID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: subscriptions ----------------
|
||||||
|
/api/v1/subscription/:
|
||||||
|
post:
|
||||||
|
tags: [subscriptions]
|
||||||
|
summary: Создать подписку
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: system_log ----------------
|
||||||
|
/api/v1/system_log/:
|
||||||
|
get:
|
||||||
|
tags: [system_log]
|
||||||
|
summary: Отфильтрованный системный лог
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
post:
|
||||||
|
tags: [system_log]
|
||||||
|
summary: Записать в системный лог
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: targets ----------------
|
||||||
|
/api/v1/targets/:
|
||||||
|
get:
|
||||||
|
tags: [targets]
|
||||||
|
summary: Список таргетов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/targets/{targetPath}/:
|
||||||
|
get:
|
||||||
|
tags: [targets]
|
||||||
|
summary: Таргет по ID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TargetPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: inspections ----------------
|
||||||
|
/api/v1/inspections/:
|
||||||
|
post:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Создать инспекцию
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Список инспекций
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/{id}:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Инспекция по ID
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdInt'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
patch:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Обновить инспекцию
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdInt'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
delete:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Удалить инспекцию
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdInt'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/created_at/daterange:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Диапазон дат создания
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/inspection_date/daterange:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Диапазон дат инспекций
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/available_responsible_users:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Доступные ответственные пользователи
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/available_inspection_dates:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Доступные даты инспекций
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/inspections/export:
|
||||||
|
get:
|
||||||
|
tags: [inspections]
|
||||||
|
summary: Экспорт инспекций
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: users / eav / releases / notes ----------------
|
||||||
|
/api/v1/users/:
|
||||||
|
get:
|
||||||
|
tags: [users]
|
||||||
|
summary: Пользователи по разрешению на ресурс
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/attribute/:
|
||||||
|
get:
|
||||||
|
tags: [eav]
|
||||||
|
summary: Получить атрибуты (EAV)
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/releases/{id}:
|
||||||
|
get:
|
||||||
|
tags: [releases]
|
||||||
|
summary: Список релизов по ID проекта
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdInt'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/notes/{service_id}/{entity_id}/{instance_id}/:
|
||||||
|
get:
|
||||||
|
tags: [notes]
|
||||||
|
summary: Заметки по сервису/сущности/инстансу
|
||||||
|
parameters:
|
||||||
|
- { name: service_id, in: path, required: true, schema: { type: string } }
|
||||||
|
- { name: entity_id, in: path, required: true, schema: { type: string } }
|
||||||
|
- { name: instance_id, in: path, required: true, schema: { type: string } }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v1: drawings ----------------
|
||||||
|
/api/v1/drawings/cross-sections:
|
||||||
|
post:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Создать сечение
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Сечения по ID инстанса
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/drawings/cross-sections/{cross_section_id}/data:
|
||||||
|
get:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Данные сечения
|
||||||
|
parameters:
|
||||||
|
- { name: cross_section_id, in: path, required: true, schema: { type: string } }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/drawings/cross-sections/{cross_section_id}:
|
||||||
|
delete:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Удалить сечение
|
||||||
|
parameters:
|
||||||
|
- { name: cross_section_id, in: path, required: true, schema: { type: string } }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/drawings/exports:
|
||||||
|
post:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Создать экспорт
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
get:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Список экспортов
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v1/drawings/exports/{export_id}:
|
||||||
|
delete:
|
||||||
|
tags: [drawings]
|
||||||
|
summary: Удалить экспорт
|
||||||
|
parameters:
|
||||||
|
- { name: export_id, in: path, required: true, schema: { type: string } }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v2 ----------------
|
||||||
|
/api/v2/resources/:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Расширенный список ресурсов (v2)
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v2/resources:
|
||||||
|
post:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Создать расширенный ресурс (v2)
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v2/resources/{id}:
|
||||||
|
get:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Расширенный ресурс по ID (v2)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
patch:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Обновить расширенный ресурс (v2)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
delete:
|
||||||
|
tags: [resources]
|
||||||
|
summary: Удалить расширенный ресурс (v2)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdPathPlain'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v2/users/:
|
||||||
|
get:
|
||||||
|
tags: [users]
|
||||||
|
summary: Пользователи по ресурсу (v2)
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v2/disks/{diskPath}/documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Список документов по диску (v2)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DiskPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- v3 / v4 ----------------
|
||||||
|
/api/v3/disks/{diskPath}/documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Упрощённый список документов по диску (v3)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/DiskPath'
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
/api/v4/disks/{disk_id}/documents:
|
||||||
|
get:
|
||||||
|
tags: [documents]
|
||||||
|
summary: Поиск документов по диску (v4)
|
||||||
|
parameters:
|
||||||
|
- { name: disk_id, in: path, required: true, schema: { type: string } }
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||||
|
|
||||||
|
# ---------------- internal ----------------
|
||||||
|
/internal/v1/documents/approving_users:
|
||||||
|
post:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Согласующие пользователи (внутренний, без аутентификации)
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
'200': { $ref: '#/components/responses/Ok' }
|
||||||
|
/internal/healthz/startup:
|
||||||
|
get:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Startup-проба (БД, опционально Valkey)
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
'200': { description: OK }
|
||||||
|
'503': { description: Service Unavailable }
|
||||||
|
/internal/healthz/live:
|
||||||
|
get:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Liveness-проба
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
'200': { description: OK }
|
||||||
|
/internal/healthz/ready:
|
||||||
|
get:
|
||||||
|
tags: [internal]
|
||||||
|
summary: Readiness-проба
|
||||||
|
security: []
|
||||||
|
responses:
|
||||||
|
'200': { description: OK }
|
||||||
|
'503': { description: Service Unavailable }
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
description: |
|
||||||
|
JWT в заголовке `Authorization: Bearer <jwt>`. В режиме Zitadel
|
||||||
|
дополнительно передаётся заголовок `Identity: Bearer <jwt>`.
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
Limit:
|
||||||
|
name: limit
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema: { type: integer, format: int64, minimum: 0 }
|
||||||
|
description: Размер страницы
|
||||||
|
Offset:
|
||||||
|
name: offset
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema: { type: integer, format: int64, minimum: 0 }
|
||||||
|
description: Смещение
|
||||||
|
DiskPath:
|
||||||
|
name: diskPath
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
description: Жадный wildcard-сегмент Fiber (может содержать `/`) — путь/ID диска
|
||||||
|
DocumentPath:
|
||||||
|
name: documentPath
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
description: Жадный wildcard-сегмент Fiber — путь/ID документа
|
||||||
|
TargetPath:
|
||||||
|
name: targetPath
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
description: Жадный wildcard-сегмент Fiber — путь/ID таргета
|
||||||
|
RemarkPath:
|
||||||
|
name: remarkPath
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
description: Жадный wildcard-сегмент Fiber — путь/ID замечания
|
||||||
|
WsId:
|
||||||
|
name: ws_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
description: UUID рабочей области
|
||||||
|
IdPath:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
IdPathPlain:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: string }
|
||||||
|
IdInt:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema: { type: integer }
|
||||||
|
description: Числовой идентификатор (Fiber-ограничение `:id<int>`)
|
||||||
|
|
||||||
|
responses:
|
||||||
|
Ok:
|
||||||
|
description: Успешный ответ (структура зависит от эндпоинта)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: {}
|
||||||
|
BadRequest:
|
||||||
|
description: Некорректный запрос
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: '#/components/schemas/AppError' }
|
||||||
|
Unauthorized:
|
||||||
|
description: Не аутентифицирован (пустое тело)
|
||||||
|
Forbidden:
|
||||||
|
description: Нет доступа
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: '#/components/schemas/AppError' }
|
||||||
|
NotFound:
|
||||||
|
description: Ресурс не найден
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: { $ref: '#/components/schemas/AppError' }
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
AppError:
|
||||||
|
type: object
|
||||||
|
description: Формат ошибки бизнес-слоя (`internal/app_errors/errors.go`)
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
example: not found
|
||||||
|
error_code:
|
||||||
|
type: string
|
||||||
|
description: Внутренний код ошибки (`GW-XXXX`)
|
||||||
|
enum: [GW-0000, GW-0001, GW-0002, GW-0003, GW-0004, GW-0014]
|
||||||
|
example: GW-0001
|
||||||
|
required: [message, error_code]
|
||||||
187
apps/flows/.env.example
Normal file
187
apps/flows/.env.example
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# flows-backend (.env.example)
|
||||||
|
# =============================================================================
|
||||||
|
# Приложение читает переменные окружения напрямую через pydantic BaseSettings
|
||||||
|
# (src/flow/config.py). У верхнеуровневого класса Settings префикса нет — его
|
||||||
|
# поля задаются переменными с именем поля в ВЕРХНЕМ регистре (напр. BASE_HOST).
|
||||||
|
# Вложенные секции конфигурируются отдельными классами со своим env_prefix
|
||||||
|
# (PG_, DJANGO_, DOCUMENTATION_, RABBITMQ_, TRACING_ и т.д.).
|
||||||
|
#
|
||||||
|
# Приложение НЕ загружает .env автоматически (нет python-dotenv/env_file) —
|
||||||
|
# экспортируйте переменные в окружение самостоятельно, напр.:
|
||||||
|
# set -a && . ./.env && set +a
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# App / общие настройки (класс Settings, без префикса)
|
||||||
|
SERVICE_NAME=review-service
|
||||||
|
SERVICE_HOST=0.0.0.0
|
||||||
|
SERVICE_PORT=8000
|
||||||
|
# Префикс за реверс-прокси (в кластере: /flows)
|
||||||
|
PROXY_PATH_PREFIX=
|
||||||
|
API_PREFIX=/api/v1
|
||||||
|
API_INTERNAL_PREFIX=/internal/v1
|
||||||
|
BASE_HOST=https://lk.sarex.io
|
||||||
|
DEBUG=False
|
||||||
|
# Проверка подписи JWT (True в кластере; False удобно для локальной разработки)
|
||||||
|
JWT_AUTH_ENABLE=True
|
||||||
|
# Таймаут gunicorn (используется в entrypoint.sh, не кодом)
|
||||||
|
TIMEOUT=120
|
||||||
|
|
||||||
|
# Feature-флаги
|
||||||
|
ENABLE_MAILINGS=True
|
||||||
|
ENABLE_MAILGUN=True
|
||||||
|
ENABLE_CELERY=True
|
||||||
|
ENABLE_EVENTS=True
|
||||||
|
ENABLE_ANALYTICS=False
|
||||||
|
SYNC_RESOURCE_ID=False
|
||||||
|
|
||||||
|
# Logger (класс LoggerSettings, префикс LOG_)
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# Database — основной PostgreSQL (класс PostgresSettings, префикс PG_)
|
||||||
|
PG_HOST=127.0.0.1
|
||||||
|
PG_PORT=6432
|
||||||
|
PG_LOGIN=flow
|
||||||
|
PG_PASSWORD=password
|
||||||
|
PG_DB=flows_db
|
||||||
|
|
||||||
|
# Documentation PG — БД сервиса документаций (класс DocumentationDBSettings, префикс DOCUMENTATION_PG_)
|
||||||
|
DOCUMENTATION_PG_HOST=127.0.0.1
|
||||||
|
DOCUMENTATION_PG_PORT=6432
|
||||||
|
DOCUMENTATION_PG_USERNAME=flow
|
||||||
|
DOCUMENTATION_PG_PASSWORD=password
|
||||||
|
DOCUMENTATION_PG_DATABASE=flows_db
|
||||||
|
|
||||||
|
# RabbitMQ (класс RabbitSettings, префикс RABBITMQ_)
|
||||||
|
RABBITMQ_HOST=localhost
|
||||||
|
RABBITMQ_PORT=5672
|
||||||
|
RABBITMQ_USERNAME=flow
|
||||||
|
RABBITMQ_PASSWORD=flow
|
||||||
|
RABBITMQ_VHOST=flows
|
||||||
|
|
||||||
|
# Celery (класс CelerySettings, префикс CELERY_; брокер берётся из RABBITMQ_*)
|
||||||
|
CELERY_QUEUE=flow
|
||||||
|
|
||||||
|
# Sarex backend (Django) (класс DjangoSettings, префикс DJANGO_)
|
||||||
|
DJANGO_USE=True
|
||||||
|
DJANGO_HOST=http://localhost:8000/api
|
||||||
|
DJANGO_TIMEOUT=60
|
||||||
|
# base64(login:password) для Basic-auth
|
||||||
|
DJANGO_TOKEN=
|
||||||
|
|
||||||
|
# Documentation service (класс DocumentationSettings, префикс DOCUMENTATION_)
|
||||||
|
DOCUMENTATION_USE=True
|
||||||
|
DOCUMENTATION_HOST=https://api.sarex.io/documentations/api/v1
|
||||||
|
DOCUMENTATION_EXTERNAL_HOST=https://api.sarex.io/documentations/api/v1
|
||||||
|
DOCUMENTATION_TIMEOUT=60
|
||||||
|
|
||||||
|
# EAV service (класс EAVSettings, префикс EAV_)
|
||||||
|
EAV_HOST=http://eav-service.eav-prod
|
||||||
|
EAV_TIMEOUT=60
|
||||||
|
|
||||||
|
# Planning management / MSP (класс PlanningManagementSettings, префикс PLANNING_)
|
||||||
|
PLANNING_USE=True
|
||||||
|
PLANNING_HOST=https://api.sarex.io/api/pm/msp
|
||||||
|
PLANNING_TIMEOUT=60
|
||||||
|
|
||||||
|
# Checklists service (класс CheckListsSettings, префикс CHECKLIST_)
|
||||||
|
CHECKLIST_USE=True
|
||||||
|
CHECKLIST_HOST=https://stage-api.sarex.io/checklists
|
||||||
|
CHECKLIST_TIMEOUT=60
|
||||||
|
|
||||||
|
# Workflows service (класс WorkflowsSettings, префикс WORKFLOWS_)
|
||||||
|
WORKFLOWS_USE=True
|
||||||
|
WORKFLOWS_HOST=https://lk.sarex.io/workflows/api/v1
|
||||||
|
WORKFLOWS_TIMEOUT=60
|
||||||
|
|
||||||
|
# Gateway / Resources (поля класса Settings, используются при SYNC_RESOURCE_ID=1)
|
||||||
|
GATEWAY_URL=https://stage-api.sarex.io/gateway
|
||||||
|
RESOURCE_URL=https://stage-api.sarex.io/resources
|
||||||
|
|
||||||
|
# Event bus (класс EventBusSettings, префикс EVENTS_)
|
||||||
|
EVENTS_HOST=ws://localhost:8000/ws
|
||||||
|
EVENTS_CONNECTION_TIMEOUT=5
|
||||||
|
EVENTS_COUNT_RETRIES=100
|
||||||
|
|
||||||
|
# Admin panel (класс AdminPanelSettings, префикс ADMIN_PANEL_)
|
||||||
|
ADMIN_PANEL_SECRET_KEY=hex
|
||||||
|
ADMIN_PANEL_TOKEN_MAX_AGE=86400
|
||||||
|
|
||||||
|
# Auth (RSA public key для проверки JWT; в кластере монтируется как JWT_PUBLIC_KEY)
|
||||||
|
JWT_PUBLIC_KEY=
|
||||||
|
|
||||||
|
# Sentry (класс SentrySettings, префикс SENTRY_)
|
||||||
|
SENTRY_DSN=
|
||||||
|
SENTRY_ENVIRONMENT=production
|
||||||
|
SENTRY_TRACES_SAMPLE_RATE=1.0
|
||||||
|
SENTRY_SEND_DEFAULT_PII=True
|
||||||
|
|
||||||
|
# Почта: SMTP (поля класса Settings; альтернатива Mailgun)
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=
|
||||||
|
FROM_EMAIL=
|
||||||
|
|
||||||
|
# OpenTelemetry / трейсинг (класс TraceSettings, префикс TRACING_)
|
||||||
|
TRACING_USE=False
|
||||||
|
TRACING_HOST=localhost:4317
|
||||||
|
TRACING_INSECURE=False
|
||||||
|
TRACING_SERVICE_NAME=flows
|
||||||
|
TRACING_ENVIRONMENT=prod
|
||||||
|
TRACING_MODULE=flows
|
||||||
|
TRACING_TEAM=team_proc
|
||||||
|
TRACING_COMPONENT=backend
|
||||||
|
|
||||||
|
# Прочее (не читается приложением, задаётся в инфраструктуре)
|
||||||
|
# ENABLE_METRICS=0
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Переменные ТОЛЬКО для celery-воркера и scheduler
|
||||||
|
# (src/worker/notifications_config.py, src/worker/sync_config.py)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Flows DB (класс FlowsDatabase, префикс FLOWS_DB_)
|
||||||
|
FLOWS_DB_HOST=127.0.0.1
|
||||||
|
FLOWS_DB_PORT=6432
|
||||||
|
FLOWS_DB_DB=flows_db
|
||||||
|
FLOWS_DB_USERNAME=flow
|
||||||
|
FLOWS_DB_PASSWORD=password
|
||||||
|
|
||||||
|
# Issues DB (класс IssuesDatabase, префикс ISSUES_DB_)
|
||||||
|
ISSUES_DB_HOST=127.0.0.1
|
||||||
|
ISSUES_DB_PORT=6432
|
||||||
|
ISSUES_DB_DB=issues_db
|
||||||
|
ISSUES_DB_USERNAME=issues
|
||||||
|
ISSUES_DB_PASSWORD=password
|
||||||
|
|
||||||
|
# RFI DB (класс RFIDatabase, префикс RFI_DB_)
|
||||||
|
RFI_DB_HOST=127.0.0.1
|
||||||
|
RFI_DB_PORT=6432
|
||||||
|
RFI_DB_DB=rfi_db
|
||||||
|
RFI_DB_USERNAME=rfi
|
||||||
|
RFI_DB_PASSWORD=password
|
||||||
|
|
||||||
|
# Django-клиент воркера (класс DjangoClient, префикс DJANGO_)
|
||||||
|
DJANGO_BASE_HOST=https://lk.sarex.io
|
||||||
|
# DJANGO_HOST — см. выше
|
||||||
|
# DJANGO_AUTH — base64(login:password); в кластере берётся из секрета django
|
||||||
|
DJANGO_AUTH=
|
||||||
|
|
||||||
|
# Resources-клиент воркера (класс ResourcesClient, префикс RESOURCES_)
|
||||||
|
RESOURCES_HOST=http://iams.iam.svc.cluster.local:8080
|
||||||
|
|
||||||
|
# Flows-клиент воркера (класс FlowsClient, префикс FLOWS_)
|
||||||
|
FLOWS_HOST=https://api.sarex.io/flows
|
||||||
|
|
||||||
|
# Настройки рассылок воркера (класс NotificationGlobalSettings, префикс NOTIFICATION_SETTINGS_)
|
||||||
|
NOTIFICATION_SETTINGS_ENABLE_MAILINGS=True
|
||||||
|
NOTIFICATION_SETTINGS_USE_MAILGUN=True
|
||||||
|
|
||||||
|
# Mailgun (класс MailgunClient, префикс MAILGUN_)
|
||||||
|
MAILGUN_HOST=https://api.mailgun.net/v3/mg.sarex.io
|
||||||
|
MAILGUN_API_KEY=
|
||||||
|
|
||||||
|
# Отправка уведомлений через Workflows (класс WorkflowsNotificationsSettings, префикс WORKFLOWS_NOTIFICATIONS_)
|
||||||
|
WORKFLOWS_NOTIFICATIONS_REGISTRY=cr.yandex/crp3ccidau046kdj8g9q
|
||||||
|
WORKFLOWS_NOTIFICATIONS_SMTP_HOST=127.0.0.1
|
||||||
|
WORKFLOWS_NOTIFICATIONS_SMTP_PORT=42069
|
||||||
|
WORKFLOWS_NOTIFICATIONS_FROM_EMAIL=hello@sarex.io
|
||||||
293
apps/flows/CONFIGURATION.md
Normal file
293
apps/flows/CONFIGURATION.md
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
# Конфигурация проекта 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 <jwt>`. Подпись проверяется публичным ключом. Пути `/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`.
|
||||||
142
apps/flows/ENDPOINTS.md
Normal file
142
apps/flows/ENDPOINTS.md
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует flows-frontend
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается модуль (микрофронтенд `flows-frontend`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
В отличие от единого реестра эндпоинтов, запросы во `flows-frontend` выполняются **точечно** из MobX-сторов (`module/store/stores/*.ts`) через общий HTTP-клиент `httpService`.
|
||||||
|
|
||||||
|
`httpService` создаётся в `module/api/http-service.ts` фабрикой `createHttpService` из `@sarex-team/sdk-js`. Клиент предоставляет методы `getRequest`, `postRequest`, `putRequest`, `patchRequest`, `deleteRequest`, каждый из которых принимает объект вида:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
httpService.getRequest({
|
||||||
|
service: "flows", // логическое имя сервиса (ключ из hosts.ts)
|
||||||
|
url: `/flows/${id}/?full=true`, // путь запроса относительно базового хоста сервиса
|
||||||
|
data: { ... }, // тело запроса (для post/put/patch)
|
||||||
|
// ...прочие опции axios
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Базовый хост сервиса подставляется по ключу `service` из `module/api/hosts.ts` в зависимости от `BUILD_ENV`. Итоговый URL = `<базовый хост сервиса>` + `url`.
|
||||||
|
|
||||||
|
Окружение выбирается переменной `BUILD_ENV` (`module/env.js`): одно из `local`, `stage`, `prod`, `preprod`, `contour`, `severstal`, `uralchem`. В `webpack.config.js` значение прокидывается в бандл через `DefinePlugin`. Значение по умолчанию при резолве хоста — `prod`.
|
||||||
|
|
||||||
|
Подключаемый удалённый модуль (`documentations`) описан отдельно в `module/api/modules-hosts.ts` и резолвится функцией `getModuleHost` (Module Federation, `remoteEntry.js`).
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значения из `module/api/hosts.ts`. Показаны `stage` и `prod`; дополнительно определены `local`, `preprod` и `contour` (в `contour` — относительные пути для изолированного контура; в `local` сервис `sarex` проксируется на `/sarex-backend`).
|
||||||
|
|
||||||
|
| Сервис (`service`) | Назначение | `stage` | `prod` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `flows` | Сервис процессов согласования (flows, reviews, steps, statuses) | `https://stage-api.sarex.io/flows/api/v1` | `https://api.sarex.io/flows/api/v1` |
|
||||||
|
| `sarex` | Локальный backend (Django `core`/`client`) | `""` (относительные пути) | `""` |
|
||||||
|
| `gateway_api_v1` | Gateway API v1 (ресурсы) | `https://stage-api.sarex.io/gateway/api/v1` | `https://api.sarex.io/gateway/api/v1` |
|
||||||
|
| `gateway_api_v2` | Gateway API v2 (пользователи) | `https://stage-api.sarex.io/gateway/api/v2` | `https://api.sarex.io/gateway/api/v2` |
|
||||||
|
| `documentations` | Сервис документации (диски, документы) | `https://stage-api.sarex.io/documentations/api/v1` | `https://api.sarex.io/documentations/api/v1` |
|
||||||
|
| `eav_api_v0` | Сервис EAV (атрибуты/схемы) | `https://stage-api.sarex.io/eav/api/v0` | `https://api.sarex.io/eav/api/v0` |
|
||||||
|
| `checklists` | Сервис чек-листов | `https://stage-api.sarex.io/checklists/api/v1` | `https://api.sarex.io/checklists/api/v1` |
|
||||||
|
| `transmittals` | Сервис передачи документации (шаблоны) | `https://stage-api.sarex.io/transmittals/api/v1` | `https://api.sarex.io/transmittals/api/v1` |
|
||||||
|
| `zitadel` | IdP (аутентификация) | `https://idp.dev.stage.sarex.io` | `https://login.sarex.io` |
|
||||||
|
|
||||||
|
Удалённые модули (`module/api/modules-hosts.ts`):
|
||||||
|
|
||||||
|
| Модуль | `stage` | `prod` |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `documentations` | `https://stage-modules.sarex.io/documentations/static/module/remoteEntry.js` | `https://modules.sarex.io/documentations/static/module/remoteEntry.js` |
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `flows` — Сервис процессов согласования
|
||||||
|
|
||||||
|
Источник: `module/store/stores/processes.ts`, `module/store/stores/resources.ts`.
|
||||||
|
|
||||||
|
| Метод (`*Request`) | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/flows/{id}/?full=true` | `processes.loadFlow` | Маршрут по id (с шагами/статусами) |
|
||||||
|
| POST | `/flows/filter/` | `processes` (фильтр) | Список маршрутов по фильтру |
|
||||||
|
| POST | `/flows/count_flows_by_resource/` | `processes` | Количество маршрутов по ресурсам |
|
||||||
|
| POST | `/flows/count_flows_by_resource/` | `processes.getCountByResourceId` | Количество маршрутов для одного ресурса |
|
||||||
|
| POST | `/flows/` | `processes.createFlow` | Создать маршрут |
|
||||||
|
| POST | `/flows/{id}/copy/?full=true` | `processes` (копирование) | Копировать маршрут |
|
||||||
|
| PUT | `/flows/{id}/?full=true` | `processes` (обновление) | Обновить маршрут |
|
||||||
|
| PATCH | `/flows/bulk-update/` | `processes.bulkUpdateFlow` | Массовое обновление маршрутов |
|
||||||
|
| POST | `/steps/` | `processes.createStep` | Создать шаг |
|
||||||
|
| PUT | `/steps/{id}/?full=true` | `processes.updateStep` | Обновить шаг |
|
||||||
|
| PATCH | `/steps/{stepId}/update_reviewers/` | `processes` | Обновить согласующих шага |
|
||||||
|
| GET | `/steps/{id}/active_reviews/` | `processes.getActiveReviewsForReviewer` | Активные review на шаге |
|
||||||
|
| POST | `/statuses/` | `processes.createStatus` | Создать статус |
|
||||||
|
| DELETE | `/statuses/{id}/` | `processes.deleteStatus` | Удалить статус |
|
||||||
|
| POST | `/reviews/count_by_resource_id/` | `resources` | Количество review по ресурсу |
|
||||||
|
|
||||||
|
### `sarex` — Локальный backend (Django)
|
||||||
|
|
||||||
|
Источник: `module/store/stores/users.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/client/settings/` | `users.getCurrentUser` | Настройки/данные текущего пользователя |
|
||||||
|
| GET | `/api/core/users/?company={id}&{query}` | `users.getUsers` / `getUsersByCompanyId` / `getAllUsersByCompanyId` | Пользователи компании |
|
||||||
|
| GET | `/api/core/admin/departments/?{query}` | `users.fetchDepartmentsSA` | Департаменты (service account) |
|
||||||
|
| GET | `/api/core/admin/departments/?company={id}&{query}` | `users.fetchDepartmentsByCompanyId` | Департаменты компании |
|
||||||
|
| GET | `/api/core/admin/positions/?{query}` | `users.fetchPositionsSA` | Должности (service account) |
|
||||||
|
| GET | `/api/core/admin/positions/?company={id}&{query}` | `users.fetchPositionsByCompanyId` | Должности компании |
|
||||||
|
|
||||||
|
### `gateway_api_v1` — Gateway API v1 (ресурсы)
|
||||||
|
|
||||||
|
Источник: `module/store/stores/resources.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/resources/?{query}` | `resources` | Список ресурсов (по фильтру) |
|
||||||
|
| GET | `/resources/?company_id={id}` | `resources` | Ресурсы компании |
|
||||||
|
|
||||||
|
### `gateway_api_v2` — Gateway API v2 (пользователи)
|
||||||
|
|
||||||
|
Источник: `module/store/stores/users.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/users/?{query}` | `users.getUsersByResourceId` | Пользователи по ресурсу |
|
||||||
|
|
||||||
|
### `documentations` — Сервис документации
|
||||||
|
|
||||||
|
Источник: `module/store/stores/documents.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/disks/{id}/documents` | `documents.fetchDocumentsByDiskId` | Документы диска |
|
||||||
|
|
||||||
|
### `eav_api_v0` — Сервис EAV (атрибуты)
|
||||||
|
|
||||||
|
Источник: `module/store/stores/attributes.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/schema/?model_name=flow&company_id={id}` | `attributes.fetchFlowsAttributes` | Схема атрибутов для модели `flow` |
|
||||||
|
|
||||||
|
### `checklists` — Сервис чек-листов
|
||||||
|
|
||||||
|
Источник: `module/store/stores/checklists.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/checklists/filter/` | `checklists.fetchChecklists` | Список чек-листов по фильтру |
|
||||||
|
|
||||||
|
### `transmittals` — Сервис передачи документации
|
||||||
|
|
||||||
|
Источник: `module/store/stores/transmittals.ts`.
|
||||||
|
|
||||||
|
| Метод | Путь | Стор / метод | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/transmittal_templates` | `transmittals` | Шаблоны трансмитталов (пагинация по `next`, фильтр по `resources`) |
|
||||||
|
|
||||||
|
### `zitadel` — IdP
|
||||||
|
|
||||||
|
Хост определён в `hosts.ts` для аутентификации через SDK; прямых вызовов из сторов в текущей версии модуля нет (используется инфраструктурой `@sarex-team/sdk-js`).
|
||||||
|
|
||||||
|
## Замечания
|
||||||
|
|
||||||
|
- Единого файла-реестра эндпоинтов (`endpoints.ts`) во `flows-frontend` нет — вызовы разбросаны по сторам `module/store/stores/*`. При добавлении нового запроса указывайте `service` строго из ключей `hosts.ts`.
|
||||||
|
- Часть путей содержит завершающий слэш и query-параметры прямо в строке `url` (напр. `/flows/{id}/?full=true`) — это соответствует поведению backend (`flows-backend`), где роуты объявлены со слэшем на конце.
|
||||||
|
- Сервис `sarex` в `stage`/`prod` имеет пустой базовый хост (`""`), то есть запросы идут по относительным путям того же origin; в `local` он проксируется на `/sarex-backend`, в `contour` — на относительные пути контура.
|
||||||
1736
apps/flows/openapi.yaml
Normal file
1736
apps/flows/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
75
apps/iam/.env.example
Normal file
75
apps/iam/.env.example
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# iams-v2 — пример переменных окружения.
|
||||||
|
# Конфиг читается из окружения (github.com/sethvargo/go-envconfig).
|
||||||
|
# Перед разбором подхватывается env-файл через godotenv: путь из ENV_FILE, иначе ".env".
|
||||||
|
# Значения ниже — дефолты из кода и примеры для локального запуска.
|
||||||
|
|
||||||
|
# App
|
||||||
|
ENVIRONMENT=local
|
||||||
|
LOG_LEVEL=info
|
||||||
|
SERVICE_NAME=iams
|
||||||
|
SERVICE_VERSION=0.0.0
|
||||||
|
|
||||||
|
# HTTP (prefix HTTP_)
|
||||||
|
HTTP_PORT=8080
|
||||||
|
# ReadBufferSize (Fiber/fasthttp), байты
|
||||||
|
HTTP_READ_BUFFER_SIZE=131072
|
||||||
|
|
||||||
|
# Database (prefix DB_)
|
||||||
|
DB_DSN=postgres://postgres:secret@localhost:5432/postgres?sslmode=disable
|
||||||
|
DB_MIGRATIONS_PATH=migrations
|
||||||
|
|
||||||
|
# Auth — внешний контур /external/api/* (prefix AUTH_)
|
||||||
|
# false ⇒ группа /external/api не регистрируется, JWT не разбирается
|
||||||
|
AUTH_ENABLED=false
|
||||||
|
|
||||||
|
# Sonyflake — machine id для генерации resource.id (prefix SONYFLAKE_)
|
||||||
|
SONYFLAKE_MACHINE_ID=1
|
||||||
|
|
||||||
|
# Zitadel Management API (prefix ZITADEL_)
|
||||||
|
# при ENABLED=true обязательны HOST, ACCESS_TOKEN, ORG_RULES_FILE
|
||||||
|
ZITADEL_ENABLED=false
|
||||||
|
ZITADEL_HOST=
|
||||||
|
ZITADEL_ACCESS_TOKEN=
|
||||||
|
ZITADEL_ORG_RULES_FILE=config/zitadel/org-rules-stage.json
|
||||||
|
|
||||||
|
# S3 (presigned GET для вложений виджета) (prefix S3_)
|
||||||
|
# при ENABLED=true обязательны ENDPOINT_URL, BUCKET_NAME, ACCESS_KEY_ID, SECRET_ACCESS_KEY
|
||||||
|
S3_ENABLED=false
|
||||||
|
S3_ENDPOINT_URL=https://storage.yandexcloud.net
|
||||||
|
S3_BUCKET_NAME=
|
||||||
|
S3_REGION=ru-central1
|
||||||
|
S3_ACCESS_KEY_ID=
|
||||||
|
S3_SECRET_ACCESS_KEY=
|
||||||
|
S3_PRESIGN_EXPIRES=1h
|
||||||
|
|
||||||
|
# Kafka (prefix KAFKA_)
|
||||||
|
KAFKA_ENABLED=false
|
||||||
|
KAFKA_BROKERS=localhost:9092
|
||||||
|
KAFKA_SECURITY_PROTOCOL=PLAINTEXT
|
||||||
|
KAFKA_SASL_MECHANISM=
|
||||||
|
KAFKA_SASL_PLAIN_USERNAME=
|
||||||
|
KAFKA_SASL_PLAIN_PASSWORD=
|
||||||
|
KAFKA_SSL_CAFILE=
|
||||||
|
# Политика имён топиков (prefix KAFKA_TOPIC_)
|
||||||
|
# По умолчанию topic = event_type. PREFIX добавляется ко всем именам.
|
||||||
|
KAFKA_TOPIC_PREFIX=
|
||||||
|
# Путь к JSON-файлу {event_type: topic} с переопределениями
|
||||||
|
KAFKA_TOPIC_OVERRIDES_FILE=
|
||||||
|
# Legacy-топик (BrokerMessage пользователя)
|
||||||
|
KAFKA_TOPIC_LEGACY_AMS_SYNC=ams-sync
|
||||||
|
|
||||||
|
# OpenTelemetry (prefix OTEL_)
|
||||||
|
OTEL_ENABLED=false
|
||||||
|
OTEL_HOST=localhost
|
||||||
|
OTEL_PORT=4317
|
||||||
|
OTEL_INSECURE=true
|
||||||
|
OTEL_SERVICE_NAME=iams
|
||||||
|
|
||||||
|
# Переменная выбора env-файла (читается до разбора конфига)
|
||||||
|
# ENV_FILE=.env
|
||||||
|
|
||||||
|
# --- Вспомогательное (не читается конфигом приложения) ---
|
||||||
|
# DSN для интеграционных тестов (общий контейнер Postgres): см. Makefile/README
|
||||||
|
# TEST_DB_DSN=postgres://postgres:secret@localhost:5432/postgres?sslmode=disable
|
||||||
|
# Включение Kafka в docker compose для сервиса http
|
||||||
|
# COMPOSE_KAFKA_ENABLED=false
|
||||||
174
apps/iam/CONFIGURATION.md
Normal file
174
apps/iam/CONFIGURATION.md
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# Конфигурация проекта iams-v2
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса **iams** (`platform/iams-v2`) — сервиса управления пользователями, ресурсами и правами доступа (IAM).
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **только через переменные окружения**. Разбор выполняется в `internal/app/config.go` (функция `app.Load`) через библиотеку [`github.com/sethvargo/go-envconfig`](https://github.com/sethvargo/go-envconfig) (структура `Config`).
|
||||||
|
|
||||||
|
Особенности разбора:
|
||||||
|
|
||||||
|
- вложенные секции задаются полями-структурами с тегом `env:", prefix=<PREFIX>_"`, напр. `DB_` → `Config.DB`, `S3_` → `Config.S3`;
|
||||||
|
- у большинства полей задан дефолт через `env:"NAME, default=..."`; поля без дефолта при отсутствии остаются нулевыми, а обязательность проверяется отдельными валидаторами (см. ниже);
|
||||||
|
- перед разбором окружения подхватывается env-файл через [`godotenv`](https://github.com/joho/godotenv): путь берётся из переменной `ENV_FILE`, иначе `.env`. Отсутствие файла **не** является ошибкой (ошибка `godotenv.Load` игнорируется), реальные значения читаются из окружения процесса.
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет, за исключением двух внешних файлов, путь к которым задаётся переменными: `ZITADEL_ORG_RULES_FILE` (JSON-правила организаций Zitadel) и `KAFKA_TOPIC_OVERRIDES_FILE` (JSON-переопределения имён топиков).
|
||||||
|
|
||||||
|
Валидация на старте (`app.Load`):
|
||||||
|
|
||||||
|
- при `S3_ENABLED=true` обязательны `S3_ENDPOINT_URL`, `S3_BUCKET_NAME`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` — иначе ошибка старта;
|
||||||
|
- при `ZITADEL_ENABLED=true` обязательны `ZITADEL_HOST`, `ZITADEL_ACCESS_TOKEN`, `ZITADEL_ORG_RULES_FILE` — иначе ошибка старта;
|
||||||
|
- при заданном `KAFKA_TOPIC_OVERRIDES_FILE` файл читается и парсится как JSON `{event_type: topic}` — при ошибке чтения/парсинга сервис не стартует.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально (бинарник) | Переменные окружения процесса + env-файл (`.env` или `ENV_FILE`), который автоматически загружается `godotenv` |
|
||||||
|
| Локально (docker compose) | `docker-compose.yml`: сервис `http` получает `DB_DSN`, `KAFKA_ENABLED`, `S3_ENABLED` из окружения хоста; Postgres — из блока `environment` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml` (universal-chart): блоки `envs` (обычные значения по окружениям `_default`/`stage`/`production`), `secretEnvs` (значения из k8s-секретов) и `volumes` (монтирование CA-сертификата Kafka) |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: переключение стенда по ветке/тегу (`workflow.rules`), общие шаблоны из `generic/common-ci`, `HELM_SET_ARGS` |
|
||||||
|
|
||||||
|
Способы запуска процессов (`cmd/*`):
|
||||||
|
|
||||||
|
| Команда | Точка входа | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `server` (docker `ENTRYPOINT`, `make build`) | `cmd/server/main.go` | HTTP API (Fiber). Флаг `-env-file=PATH` переопределяет `ENV_FILE` |
|
||||||
|
| `cli migrate` | `cmd/cli/main.go` | Прогон миграций БД (`golang-migrate`). Требует `DB_DSN`; путь миграций — `DB_MIGRATIONS_PATH` |
|
||||||
|
| `seed` | `cmd/seed/main.go` | Наполнение БД тестовыми данными (флаги `--seed`, `--resources`, `--users`, `--tenant-id`, `--type-id`) |
|
||||||
|
|
||||||
|
Docker-образ (`docker/httpserver/Dockerfile`) собирает статический бинарник `server` (scratch-образ) и копирует каталог `config/` (файлы правил Zitadel). Миграции применяются отдельной командой `cli migrate` (в локальном сценарии — целью `make postgres-up`).
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
В столбце «Переменная» указано полное имя (с учётом префикса секции). Дефолт `—` означает, что в коде значения по умолчанию нет (поле остаётся нулевым, если не задано).
|
||||||
|
|
||||||
|
### App (верхний уровень, без префикса)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENVIRONMENT` | string | `local` | Окружение развёртывания (`local`/`stage`/`prod`) |
|
||||||
|
| `LOG_LEVEL` | string | `info` | Уровень логирования (`debug`/`info`/`warn`/`error`) |
|
||||||
|
| `SERVICE_NAME` | string | `iams` | Имя сервиса |
|
||||||
|
| `SERVICE_VERSION` | string | `0.0.0` | Версия сервиса |
|
||||||
|
|
||||||
|
### HTTP (`HTTP_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `HTTP_PORT` | string | `8080` | Порт HTTP-сервера |
|
||||||
|
| `HTTP_READ_BUFFER_SIZE` | int | `131072` | Размер буфера чтения запроса (Fiber/fasthttp), байты |
|
||||||
|
|
||||||
|
### Database (`DB_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DB_DSN` | string | — | DSN PostgreSQL (`postgres://user:pass@host:port/db?sslmode=...`). Обязателен для работы БД и команды `cli migrate` |
|
||||||
|
| `DB_MIGRATIONS_PATH` | string | `migrations` | Путь к каталогу SQL-миграций |
|
||||||
|
|
||||||
|
### Auth (`AUTH_*`)
|
||||||
|
|
||||||
|
Конфигурация внешнего контура `/external/api/*`.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `AUTH_ENABLED` | bool | `false` | При `false` внешний контур не регистрируется и пользовательская авторизация не выполняется; внутренние эндпоинты работают без изменений |
|
||||||
|
|
||||||
|
### Sonyflake (`SONYFLAKE_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `SONYFLAKE_MACHINE_ID` | uint16 | `1` | Machine ID генератора идентификаторов `resource.id` для новых записей |
|
||||||
|
|
||||||
|
### Zitadel (`ZITADEL_*`)
|
||||||
|
|
||||||
|
Management API (Service User с правами администратора). При `ENABLED=true` `HOST`, `ACCESS_TOKEN`, `ORG_RULES_FILE` обязательны.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ZITADEL_ENABLED` | bool | `false` | Включить интеграцию с Zitadel |
|
||||||
|
| `ZITADEL_HOST` | string | — | Хост Zitadel (напр. `https://login.sarex.io`) |
|
||||||
|
| `ZITADEL_ACCESS_TOKEN` | string | — | Access token сервисного пользователя |
|
||||||
|
| `ZITADEL_ORG_RULES_FILE` | string | — | Путь к JSON-файлу правил организаций (`config/zitadel/org-rules-<env>.json`) |
|
||||||
|
|
||||||
|
### S3 (`S3_*`)
|
||||||
|
|
||||||
|
Presigned GET для вложений виджета в приватном S3-совместимом бакете. При `ENABLED=true` `ENDPOINT_URL`, `BUCKET_NAME`, `ACCESS_KEY_ID`, `SECRET_ACCESS_KEY` обязательны.
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `S3_ENABLED` | bool | `false` | Включить S3-интеграцию |
|
||||||
|
| `S3_ENDPOINT_URL` | string | — | Эндпоинт S3 (напр. `https://storage.yandexcloud.net`) |
|
||||||
|
| `S3_BUCKET_NAME` | string | — | Имя бакета |
|
||||||
|
| `S3_REGION` | string | `ru-central1` | Регион |
|
||||||
|
| `S3_ACCESS_KEY_ID` | string | — | Access key |
|
||||||
|
| `S3_SECRET_ACCESS_KEY` | string | — | Secret key |
|
||||||
|
| `S3_PRESIGN_EXPIRES` | duration | `1h` | Срок жизни presigned-ссылки (Go duration, напр. `1h`, `15m`) |
|
||||||
|
|
||||||
|
### Kafka (`KAFKA_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `KAFKA_ENABLED` | bool | `false` | Включить Kafka-продюсер |
|
||||||
|
| `KAFKA_BROKERS` | string | `localhost:9092` | Список брокеров |
|
||||||
|
| `KAFKA_SECURITY_PROTOCOL` | string | `PLAINTEXT` | Протокол (`PLAINTEXT`/`SASL_SSL`/...) |
|
||||||
|
| `KAFKA_SASL_MECHANISM` | string | — | SASL-механизм (напр. `SCRAM-SHA-512`) |
|
||||||
|
| `KAFKA_SASL_PLAIN_USERNAME` | string | — | SASL-логин |
|
||||||
|
| `KAFKA_SASL_PLAIN_PASSWORD` | string | — | SASL-пароль |
|
||||||
|
| `KAFKA_SSL_CAFILE` | string | — | Путь к CA-сертификату для SSL |
|
||||||
|
|
||||||
|
Политика имён топиков (`KAFKA_TOPIC_*`):
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `KAFKA_TOPIC_PREFIX` | string | — | Префикс, добавляемый ко всем именам топиков. По умолчанию `topic = event_type` |
|
||||||
|
| `KAFKA_TOPIC_OVERRIDES_FILE` | string | — | Путь к JSON-файлу `{event_type: topic}` с переопределениями. Читается на старте |
|
||||||
|
| `KAFKA_TOPIC_LEGACY_AMS_SYNC` | string | `ams-sync` | Legacy-топик для `BrokerMessage` пользователя |
|
||||||
|
|
||||||
|
### OpenTelemetry (`OTEL_*`)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `OTEL_ENABLED` | bool | `false` | Включить трейсинг |
|
||||||
|
| `OTEL_HOST` | string | `localhost` | Хост OTLP/gRPC-коллектора |
|
||||||
|
| `OTEL_PORT` | string | `4317` | Порт коллектора |
|
||||||
|
| `OTEL_INSECURE` | bool | `true` | Небезопасное (без TLS) подключение к коллектору |
|
||||||
|
| `OTEL_SERVICE_NAME` | string | `iams` | Имя сервиса в трейсах |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Чарт — `universal-chart` (сервис `iams`). Обычные значения задаются в блоке `envs` с профилями `_default`/`stage`/`production` и содержат те же переменные `ENVIRONMENT`, `LOG_LEVEL`, `AUTH_ENABLED`, `HTTP_*`, `DB_MIGRATIONS_PATH`, `ZITADEL_*`, `S3_*`, `KAFKA_*`, `OTEL_*` (различаются адресами БД/брокеров/коллектора, бакетом, доменом Zitadel и путём к файлу правил).
|
||||||
|
|
||||||
|
Значения из секретов (блок `secretEnvs`, монтируются как env через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (`secretName`) | Ключ (`secretKey`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DB_DSN` | `iams-secret` | `db-dsn` |
|
||||||
|
| `ZITADEL_ACCESS_TOKEN` | `iams-secret` | `zitadel-access-token` |
|
||||||
|
| `KAFKA_SASL_PLAIN_USERNAME` | `iams-secret` | `kafka-sasl-plain-username` |
|
||||||
|
| `KAFKA_SASL_PLAIN_PASSWORD` | `iams-secret` | `kafka-sasl-plain-password` |
|
||||||
|
| `S3_ACCESS_KEY_ID` | `yc-s3-secret` | `key_id` |
|
||||||
|
| `S3_SECRET_ACCESS_KEY` | `yc-s3-secret` | `access_key` |
|
||||||
|
|
||||||
|
Помимо env, чарт монтирует CA-сертификат Kafka из секрета `ya-ca-secret` как файл `/etc/ca-certificates/Yandex/ca-cert` — именно на него указывает `KAFKA_SSL_CAFILE` в конфигурации стенда/прода.
|
||||||
|
|
||||||
|
Прочие значения чарта (не переменные приложения): `deployment.*` (имя, порт `8080`, `replicaCount`: `_default=1`, `production=4`), `image.name` (`cr.yandex/.../iams:latest`), `service.*` (порт/targetPort `8080`).
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml@apps-business`) и переключает окружение по ветке/тегу через `workflow.rules`:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace | Chart version |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `platform` | `0.0.1-stage` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `production` | `iam` | `0.0.1-prod` |
|
||||||
|
| merge request | — | — | сборка образа отключена (`ENABLE_BUILD_IMAGE=false`) |
|
||||||
|
|
||||||
|
Ключевые переменные пайплайна: `SERVICE_NAME=iams`, `DOCKERFILE_PATH=./docker/httpserver/Dockerfile`, `RELEASE_NAME`, `CHART_NAME`, `K8S_HUSTLER_BRANCH`, `HELM_SET_ARGS` (образ, `global.env`, `commitSha`, ссылки на GitLab). Джобы `lint` (`go vet` + `golangci-lint`) и `test` (`go test ./...`) выполняются на merge request.
|
||||||
|
|
||||||
|
## Замечания и минимальный набор для локального запуска
|
||||||
|
|
||||||
|
- Приложение **загружает env-файл автоматически** (`godotenv`): достаточно положить `.env` рядом с бинарником или указать путь через `ENV_FILE` / флаг `-env-file`. Отсутствие файла не является ошибкой.
|
||||||
|
- Три интеграции выключены по умолчанию (`ENABLED=false`): `AUTH`, `ZITADEL`, `S3`, `KAFKA`, `OTEL`. Включение любой из `S3`/`ZITADEL` требует заполнения обязательных полей (см. валидаторы выше), иначе сервис не стартует.
|
||||||
|
- Для локального запуска минимально необходимо задать `DB_DSN` (Postgres поднимается через `docker compose up -d postgres`, миграции — `make postgres-up` → `cli migrate`). Остальные секции можно оставить с дефолтами (`ENABLED=false`).
|
||||||
|
- Готовые значения-примеры для всех переменных приведены в `.env.example`.
|
||||||
207
apps/iam/ENDPOINTS.md
Normal file
207
apps/iam/ENDPOINTS.md
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
# Эндпоинты сервиса iams-v2
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты, которые **предоставляет** сервис `iams` (IAM: пользователи, ресурсы/проекты, права доступа). В отличие от фронтенд-модулей, здесь описан контракт самого сервиса.
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Сервер — Fiber (`internal/controller/http/server.go`). Маршруты регистрируются двумя наборами:
|
||||||
|
|
||||||
|
- **Внутренний контур** (`RegisterAPIRoutes`) — без аутентификации на уровне приложения, доступ ограничивается сетевым слоем (не публикуется через ingress). Базовые группы: `/api/v0`, `/api/admin/v0`, `/api/v1`, `/api/v2`.
|
||||||
|
- **Внешний контур** (`RegisterExternalAPIRoutes`) — регистрируется только при `AUTH_ENABLED=true`. Перед маршрутами выполняется разбор JWT (`AuthMiddleware`) и пометка контекста (`ExternalAPIMiddleware`, для админ-группы дополнительно `ExternalUserAdminAPIMiddleware`). Базовые группы: `/external/api/v0`, `/external/api/admin/v0`, `/external/api/v1`, `/external/api/v2`. Внешний контур публикует **подмножество** внутренних маршрутов, часть — только на чтение.
|
||||||
|
|
||||||
|
Глобальные middleware: `requestid`, `recover`, OTel (при `OTEL_ENABLED` и заданном `OTEL_SERVICE_NAME`), логирование.
|
||||||
|
|
||||||
|
### Аутентификация (внешний контур)
|
||||||
|
|
||||||
|
Токен передаётся заголовком `Authorization: Bearer <jwt>` (`internal/controller/http/jwt/parser.go`). **Подпись токена не проверяется** — доверие делегируется вышестоящему gateway/virtual service. Схема разбора выбирается по заголовку `Identity`:
|
||||||
|
|
||||||
|
- заголовок `Identity` отсутствует/пуст → legacy-формат (Django SimpleJWT);
|
||||||
|
- заголовок `Identity` задан → Zitadel (клейм `urn:zitadel:iam:user:metadata`, значения полей — base64).
|
||||||
|
|
||||||
|
### Формат ответов и ошибок
|
||||||
|
|
||||||
|
Успешные ответы — JSON (списки: `{ count, results }`; часть эндпоинтов возвращает объект напрямую). Ошибки — JSON `{ "error": "...", "id": "..." }` (`httpx.Response`). Доменный `Kind` маппится на HTTP-статус (`httpx/error_resolve.go`): `InvalidArgument/OutOfRange`→`400`, `Unauthenticated`→`401`, `PermissionDenied`→`403`, `NotFound`→`404`, `Aborted/AlreadyExists`→`409`, `PreconditionFailed`→`412`, `ResourceExhausted`→`429`, `Unavailable`→`503`, `Internal/Unknown/DataLoss`→`500`. Идентификатор запроса — заголовок из `requestid`.
|
||||||
|
|
||||||
|
### Пагинация и фильтры
|
||||||
|
|
||||||
|
Списки — через query `limit`/`offset` (по умолчанию `limit=1000`). Дополнительные фильтры задаются query-параметрами (напр. для `/resource`: `parent_id`, `type`, `tenant_id`, `name`, `code`, `public_id`, `target_id`, `service_accounts`; для `/users`: `username`, `search`, `is_active`, `is_staff`, `is_superuser`, `service_account_id`, `id`, `company_id`, `departments`, `positions`, `groups`).
|
||||||
|
|
||||||
|
## Служебные эндпоинты
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/health` | Health-check: проверка БД и (при включении) Kafka. `200` — ok, `503` — недоступность зависимости |
|
||||||
|
|
||||||
|
## Внутренний контур
|
||||||
|
|
||||||
|
### Users (`/api/v0/users`, `/api/admin/v0`, `/api/v1`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/users` | Список пользователей (фильтры, пагинация) |
|
||||||
|
| POST | `/api/v0/users` | Создать пользователя (если включён use case) |
|
||||||
|
| PATCH | `/api/v0/users` | Массовое обновление пользователей (legacy bulk_update) |
|
||||||
|
| GET | `/api/v0/users/:id` | Пользователь по id |
|
||||||
|
| PUT | `/api/v0/users/:id` | Полное обновление пользователя |
|
||||||
|
| PATCH | `/api/v0/users/:id` | Частичное обновление пользователя |
|
||||||
|
| DELETE | `/api/v0/users/:id` | Удалить пользователя |
|
||||||
|
| POST | `/api/admin/v0/users/activation` | Активация/деактивация пользователей компании |
|
||||||
|
| POST | `/api/v1/users-with-resources` | Пользователи с их ресурсами (тело: `tenant_id`, `id[]`) |
|
||||||
|
| GET | `/api/v1/users-grouped-by-resource/` | Пользователи, сгруппированные по ресурсу (если включён use case) |
|
||||||
|
|
||||||
|
### Permission check (`/api/v0`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| POST | `/api/v0/permissions-check` | Проверка набора прав пользователя (`user_id`, `checks[]`) |
|
||||||
|
|
||||||
|
### Groups (`/api/v0/groups`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/groups` | Список групп |
|
||||||
|
| POST | `/api/v0/groups` | Создать группу |
|
||||||
|
| POST | `/api/v0/groups/search` | Поиск групп (пагинация, сортировка, фильтры) |
|
||||||
|
| GET | `/api/v0/groups/:id` | Группа по id |
|
||||||
|
| PUT | `/api/v0/groups/:id` | Обновить группу |
|
||||||
|
| PATCH | `/api/v0/groups/:id` | Частично обновить группу |
|
||||||
|
| DELETE | `/api/v0/groups/:id` | Удалить группу |
|
||||||
|
|
||||||
|
### Positions (`/api/v0/positions`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/positions` | Список должностей |
|
||||||
|
| POST | `/api/v0/positions` | Создать должность |
|
||||||
|
| GET | `/api/v0/positions/:id` | Должность по id |
|
||||||
|
| PUT | `/api/v0/positions/:id` | Обновить должность |
|
||||||
|
| PATCH | `/api/v0/positions/:id` | Частично обновить должность |
|
||||||
|
| DELETE | `/api/v0/positions/:id` | Удалить должность |
|
||||||
|
|
||||||
|
### Departments (`/api/v0/departments`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/departments` | Список подразделений |
|
||||||
|
| POST | `/api/v0/departments` | Создать подразделение |
|
||||||
|
| GET | `/api/v0/departments/:id` | Подразделение по id |
|
||||||
|
| PUT | `/api/v0/departments/:id` | Обновить подразделение |
|
||||||
|
| PATCH | `/api/v0/departments/:id` | Частично обновить подразделение |
|
||||||
|
| DELETE | `/api/v0/departments/:id` | Удалить подразделение |
|
||||||
|
|
||||||
|
### Django permissions (`/api/v0/permissions`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/permissions` | Список permission'ов |
|
||||||
|
| POST | `/api/v0/permissions` | Создать permission |
|
||||||
|
| POST | `/api/v0/permissions/search` | Поиск permission'ов |
|
||||||
|
| GET | `/api/v0/permissions/:id` | Permission по id |
|
||||||
|
| DELETE | `/api/v0/permissions/:id` | Удалить permission |
|
||||||
|
|
||||||
|
### Resource types v0 (`/api/v0/resource-types`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/resource-types` | Список типов ресурсов |
|
||||||
|
| POST | `/api/v0/resource-types` | Создать тип |
|
||||||
|
| GET | `/api/v0/resource-types/:id` | Тип по id |
|
||||||
|
| PUT | `/api/v0/resource-types/:id` | Обновить тип |
|
||||||
|
| PATCH | `/api/v0/resource-types/:id` | Частично обновить тип |
|
||||||
|
| DELETE | `/api/v0/resource-types/:id` | Удалить тип |
|
||||||
|
|
||||||
|
### Resources v0 (`/api/v0/resources`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/resources` | Список ресурсов (legacy v0) |
|
||||||
|
| POST | `/api/v0/resources` | Создать ресурс |
|
||||||
|
| GET | `/api/v0/resources/:id` | Ресурс по id |
|
||||||
|
| PUT | `/api/v0/resources/:id` | Обновить ресурс |
|
||||||
|
| PATCH | `/api/v0/resources/:id` | Частично обновить ресурс |
|
||||||
|
| DELETE | `/api/v0/resources/:id` | Удалить ресурс |
|
||||||
|
|
||||||
|
### Projects v0 (`/api/v0/projects`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/projects` | Список проектов |
|
||||||
|
| POST | `/api/v0/projects` | Создать проект |
|
||||||
|
| GET | `/api/v0/projects/:publicID` | Проект по public id |
|
||||||
|
| PUT | `/api/v0/projects/:publicID` | Обновить проект |
|
||||||
|
| PATCH | `/api/v0/projects/:publicID` | Частично обновить проект |
|
||||||
|
| DELETE | `/api/v0/projects/:publicID` | Удалить проект |
|
||||||
|
|
||||||
|
### Widgets v0 (`/api/v0/widgets`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v0/widgets` | Список виджетов |
|
||||||
|
| POST | `/api/v0/widgets` | Создать виджет |
|
||||||
|
| GET | `/api/v0/widgets/:publicID` | Виджет по public id |
|
||||||
|
| PUT | `/api/v0/widgets/:publicID` | Обновить виджет |
|
||||||
|
| PATCH | `/api/v0/widgets/:publicID` | Частично обновить виджет |
|
||||||
|
| DELETE | `/api/v0/widgets/:publicID` | Удалить виджет |
|
||||||
|
|
||||||
|
### Resources v1 (`/api/v1/resource`, `/api/v1/targets`, `/api/v1/service-accounts`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/resource` | Список ресурсов (v1) |
|
||||||
|
| POST | `/api/v1/resource` | Создать ресурс |
|
||||||
|
| GET | `/api/v1/resource/:publicID` | Ресурс по public id |
|
||||||
|
| PUT | `/api/v1/resource/:publicID` | Обновить ресурс |
|
||||||
|
| PATCH | `/api/v1/resource/:publicID` | Частично обновить ресурс |
|
||||||
|
| DELETE | `/api/v1/resource/:publicID` | Удалить ресурс |
|
||||||
|
| GET | `/api/v1/targets/:target_id/resource` | Ресурс по target id (только внутренний) |
|
||||||
|
| GET | `/api/v1/resources-grouped-by-sa` | Ресурсы, сгруппированные по сервисному аккаунту (если включено) |
|
||||||
|
| GET | `/api/v1/service-accounts` | Список сервисных аккаунтов |
|
||||||
|
|
||||||
|
### Widgets v2 (`/api/v2/resource`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v2/resource` | Список виджетов (v2) |
|
||||||
|
| POST | `/api/v2/resource` | Создать виджет |
|
||||||
|
| GET | `/api/v2/resource/:publicID` | Виджет по public id |
|
||||||
|
| PUT | `/api/v2/resource/:publicID` | Обновить виджет |
|
||||||
|
| PATCH | `/api/v2/resource/:publicID` | Частично обновить виджет |
|
||||||
|
| DELETE | `/api/v2/resource/:publicID` | Удалить виджет |
|
||||||
|
|
||||||
|
### Permissions v1 (`/api/v1`)
|
||||||
|
|
||||||
|
| Метод | Путь | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/resource_permission` | Список прав на ресурсы |
|
||||||
|
| POST | `/api/v1/resource_permission` | Создать права (пакет `items[]`) |
|
||||||
|
| GET | `/api/v1/resource_permission/:id` | Право по id |
|
||||||
|
| DELETE | `/api/v1/resource_permission/:id` | Удалить право |
|
||||||
|
| PATCH | `/api/v1/permissions-bulk` | Массовое изменение прав (`tenant_id`, `permissions`) |
|
||||||
|
| GET | `/api/v1/company_resource_permission` | Список прав компании на ресурсы |
|
||||||
|
| POST | `/api/v1/company_resource_permission` | Создать права компании (пакет `items[]`) |
|
||||||
|
| GET | `/api/v1/company_resource_permission/:id` | Право компании по id |
|
||||||
|
| DELETE | `/api/v1/company_resource_permission/:id` | Удалить право компании |
|
||||||
|
|
||||||
|
## Внешний контур (`/external/api/*`, только при `AUTH_ENABLED=true`)
|
||||||
|
|
||||||
|
Публикуется подмножество внутренних маршрутов; часть — только на чтение. Пути идентичны внутренним, но с префиксом `/external/api`.
|
||||||
|
|
||||||
|
| Группа | Маршруты | Отличия от внутреннего контура |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `/external/api/v0/users` | `GET /`, `GET /:id`, `PATCH /` | Только чтение + массовый PATCH (bulk) |
|
||||||
|
| `/external/api/admin/v0/users` | `POST /activation`, полный CRUD `/users` | Полный доступ (админ-группа) |
|
||||||
|
| `/external/api/admin/v0/positions` | `/positions` (CRUD) | Как внутренний |
|
||||||
|
| `/external/api/admin/v0/departments` | `/departments` (CRUD) | Как внутренний |
|
||||||
|
| `/external/api/admin/v0/groups` | `/groups` (CRUD + search) | Как внутренний |
|
||||||
|
| `/external/api/admin/v0/permissions` | `/permissions` (django) | Как внутренний |
|
||||||
|
| `/external/api/v0/resource-types` | `/resource-types` (CRUD) | Как внутренний |
|
||||||
|
| `/external/api/v0/resources` | `/resources` (CRUD, v0) | Как внутренний |
|
||||||
|
| `/external/api/v0/projects` | `GET /`, `GET /:publicID` | Только чтение |
|
||||||
|
| `/external/api/v0/widgets` | `/widgets` (CRUD) | Как внутренний |
|
||||||
|
| `/external/api/v1/resource` | `GET /`, `GET /:publicID` | Только чтение |
|
||||||
|
| `/external/api/v2/resource` | `/resource` (виджеты v2, CRUD) | Как внутренний |
|
||||||
|
| `/external/api/v1/resource_permission` | CRUD-подмножество | Как внутренний |
|
||||||
|
| `/external/api/v1/permissions-bulk` | `PATCH /` | Как внутренний |
|
||||||
|
| `/external/api/v1/company_resource_permission` | CRUD-подмножество | Как внутренний |
|
||||||
|
|
||||||
|
> Часть маршрутов регистрируется условно — только если соответствующий хендлер/use case подключён при инициализации сервера (проверки `Has*` в хендлерах). При отсутствии сервиса группа не регистрируется.
|
||||||
1170
apps/iam/openapi.yaml
Normal file
1170
apps/iam/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
65
apps/inspections/.env.example
Normal file
65
apps/inspections/.env.example
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Общие настройки процесса
|
||||||
|
PYTHONPATH=src
|
||||||
|
PICCOLO_CONF=db.config
|
||||||
|
|
||||||
|
# App
|
||||||
|
DEBUG=true
|
||||||
|
SERVICE_URL=https://stage.sarex.io
|
||||||
|
|
||||||
|
# HTTP App
|
||||||
|
HTTP_APP_HOST=0.0.0.0
|
||||||
|
HTTP_APP_PORT=8000
|
||||||
|
HTTP_APP_ROOT_PATH=""
|
||||||
|
HTTP_APP_WORKERS=1
|
||||||
|
HTTP_APP_ADMIN_ENABLE=true
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_HOST=postgres
|
||||||
|
DATABASE_PORT=5432
|
||||||
|
DATABASE_NAME=postgres
|
||||||
|
DATABASE_USER=postgres
|
||||||
|
DATABASE_PASSWORD=postgres
|
||||||
|
|
||||||
|
# Kafka
|
||||||
|
KAFKA_HOST=host
|
||||||
|
KAFKA_USERNAME=username
|
||||||
|
KAFKA_PASSWORD=password
|
||||||
|
# Если задан KAFKA_SSL_CERT (для http-приложения) или KAFKA_SSL_CAFILE (для kafka-consumer) —
|
||||||
|
# подключение идёт по SASL-SCRAM-SHA-512 поверх TLS, иначе без SSL.
|
||||||
|
KAFKA_SSL_CERT=""
|
||||||
|
KAFKA_SSL_CAFILE=ssl_cafile
|
||||||
|
KAFKA_EAV_ASSETS_TOPIC=eav_assets_topic
|
||||||
|
|
||||||
|
# OpenTelemetry
|
||||||
|
OTEL_ENABLE=false
|
||||||
|
OTEL_URL=http://signoz-otel-collector-external.signoz.svc.cluster.local:4317
|
||||||
|
OTEL_SERVICE_NAME=inspections-backend.inspections-stage
|
||||||
|
OTEL_INSECURE=true
|
||||||
|
|
||||||
|
# Auth (JWT)
|
||||||
|
# Если false — middleware аутентификации не подключается, используется дефолтный пользователь.
|
||||||
|
JWT_AUTH_ENABLE=false
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
NOTIFICATIONS_ENABLE=false
|
||||||
|
NOTIFICATIONS_EMAIL_FROM=hello@sarex.io
|
||||||
|
|
||||||
|
# Sarex backend
|
||||||
|
SAREX_BACKEND_URL=https://stage.sarex.io
|
||||||
|
SAREX_BACKEND_TIMEOUT=30
|
||||||
|
# base64(login:password)
|
||||||
|
SAREX_BACKEND_AUTH=base64(login:password)
|
||||||
|
|
||||||
|
# EAV (сервис атрибутов)
|
||||||
|
EAV_URL=https://stage-api.sarex.io/eav
|
||||||
|
EAV_TIMEOUT=30
|
||||||
|
|
||||||
|
# Workflows (процессы / отправка email)
|
||||||
|
WORKFLOWS_URL=http://workflows-api-service.processing-stage
|
||||||
|
WORKFLOWS_TIMEOUT=30
|
||||||
|
WORKFLOWS_EMAIL_DOCKER_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/notification:email
|
||||||
|
|
||||||
|
# Мобильное приложение (версии)
|
||||||
|
MOBILE_APP_CURRENT_VERSION=1.0.0
|
||||||
|
MOBILE_APP_RECOMMENDED_VERSION=1.0.0
|
||||||
|
MOBILE_APP_REQUIRED_VERSION=1.0.0
|
||||||
216
apps/inspections/CONFIGURATION.md
Normal file
216
apps/inspections/CONFIGURATION.md
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
# Конфигурация проекта 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` локально
|
||||||
115
apps/inspections/ENDPOINTS.md
Normal file
115
apps/inspections/ENDPOINTS.md
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует inspections-frontend
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается модуль (микрофронтенд `inspections-frontend`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Запросы выполняются через общий `httpService` (`module/api/http-service.ts`), созданный фабрикой `createHttpService` из `@sarex-team/sdk-js` (поверх `axios`). У сервиса есть методы `getRequest`, `postRequest`, `patchRequest`, `deleteRequest`, каждый из которых принимает объект с полями:
|
||||||
|
|
||||||
|
- `service` — логическое имя сервиса (см. таблицу хостов ниже);
|
||||||
|
- `url` — путь запроса (дописывается к базовому хосту сервиса);
|
||||||
|
- `data` — тело запроса (для POST/PATCH);
|
||||||
|
- `axiosConfig` — доп. настройки axios (`params`, `paramsSerializer` и т.п.);
|
||||||
|
- `showErrorNotification` — показывать ли уведомление об ошибке.
|
||||||
|
|
||||||
|
Базовый хост подставляется по паре «`service` + окружение». Окружение определяется глобальной переменной сборки `BUILD_ENV` (`local`/`stage`/`preprod`/`prod`/`contour`); при её отсутствии используется `prod` (`const buildEnv = BUILD_ENV ?? "prod"`). В режиме `local` для http-сервиса выставляется `type: "original"`. `BUILD_ENV` задаётся при сборке (напр. `BUILD_ENV=stage npm start`) и прокидывается через webpack DefinePlugin.
|
||||||
|
|
||||||
|
Определения запросов сгруппированы по файлам в `module/api/` (`inspections.ts`, `assets.ts`, `Issues.ts`, `premises-api.ts`) и по стор-файлам в `module/Inspections/store/` (`inspections.ts`, `filters.ts`, `resource.ts`, `calendar.ts`, `issuesStore.ts`).
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значения из `module/api/hosts.ts`. Итоговый URL = `<базовый хост сервиса>` + `url` запроса.
|
||||||
|
|
||||||
|
| Сервис (`service`) | Назначение | `stage` | `prod` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `inspections` | Сервис событий/инспекций (этот бэкенд) | `https://stage-api.sarex.io/inspections/api/v1` | `https://api.sarex.io/inspections/api/v1` |
|
||||||
|
| `sarexApi` | Gateway/API Sarex (`/gateway`) | `https://stage-api.sarex.io` | `https://api.sarex.io` |
|
||||||
|
| `sarex` | Локальный сервис данных (`/api/core`, `/api/client`) | `""` (относительные пути) | `""` |
|
||||||
|
| `eavV0` | Сервис атрибутов EAV, API v0 | `https://stage-api.sarex.io/eav/api/v0` | `https://api.sarex.io/eav/api/v0` |
|
||||||
|
| `eavV4` | Сервис атрибутов EAV, API v4 (ассеты) | `https://stage-api.sarex.io/eav/api/v4` | `https://api.sarex.io/eav/api/v4` |
|
||||||
|
| `issues` | Сервис замечаний | `https://stage-api.sarex.io/issues/api` | `https://api.sarex.io/issues/api` |
|
||||||
|
| `premises` | Сервис помещений | `https://stage-api.sarex.io/premises/api/v1` | `https://api.sarex.io/premises/api/v1` |
|
||||||
|
| `zitadel` | IdP (аутентификация) | `https://idp.dev.stage.sarex.io` | `https://login.sarex.io` |
|
||||||
|
|
||||||
|
> Также определены окружения `local`, `preprod` и `contour`. В `local` сервис `sarex` проксируется на `/sarex-backend`, а `inspections`/`eav`/`issues`/`premises` указывают на стейдж. В `contour` все хосты пустые (относительные пути для изолированного контура). Сервис `zitadel` в hosts объявлен, но прямых запросов из модуля к нему нет — аутентификация обрабатывается на уровне SDK/платформы.
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `inspections` — Сервис событий/инспекций
|
||||||
|
|
||||||
|
Базовый хост уже включает `/api/v1`, поэтому в путях ниже он не повторяется.
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/inspections/` | `store/resource.ts` | Создание/список событий |
|
||||||
|
| POST | `/inspections/` | `store/resource.ts` | Создать событие |
|
||||||
|
| POST | `/inspections/filter/` | `store/resource.ts`, `store/calendar.ts` | Список событий с фильтрами и пагинацией в теле |
|
||||||
|
| GET | `/inspections/{id}/` | `api/inspections.ts`, `store/resource.ts` | Событие по id |
|
||||||
|
| PATCH | `/inspections/{id}/` | `api/inspections.ts` | Частичное обновление события |
|
||||||
|
| GET | `/inspections/types/` | `api/inspections.ts` | Типы событий компании (query `company_id`) |
|
||||||
|
| POST | `/inspections/status-count/` | `store/inspections.ts` | Счётчики по статусам (фильтры в теле) |
|
||||||
|
| POST | `/inspections/filter-options/` | `store/filters.ts` | Доступные значения фильтров |
|
||||||
|
| GET | `/inspections/aggregate/created_at/minmax/` | `store/filters.ts` | Мин/макс по дате создания |
|
||||||
|
| GET | `/inspections/aggregate/inspection_dt/minmax/` | `store/filters.ts` | Мин/макс по дате проведения |
|
||||||
|
| GET | `/inspections/change-history/` | `api/inspections.ts` | История изменений (пагинация, фильтры в query) |
|
||||||
|
| GET | `/inspections/export/` | `store/inspections.ts` | Экспорт событий (xlsx) |
|
||||||
|
| POST | `/inspections/unavailable-dates/` | `api/inspections.ts` | Недоступные даты для исполнителей |
|
||||||
|
| POST | `/inspections/unavailable-users/` | `api/inspections.ts` | Недоступные исполнители на интервал |
|
||||||
|
|
||||||
|
### `sarexApi` — Gateway/API Sarex
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/gateway/api/v1/resources/?company_id={companyId}` | `store/inspections.ts` | Ресурсы (проекты) компании |
|
||||||
|
| GET | `/gateway/api/v2/users/` | `store/issuesStore.ts`, `store/resource.ts` | Пользователи (с пагинацией/фильтрами) |
|
||||||
|
| GET | `/gateway/api/v1/attachments/?company_id={companyId}&instance_id={id}&model_name=inspection` | `store/resource.ts` | Вложения события |
|
||||||
|
| POST | `/gateway/api/v1/attachments/` | `store/resource.ts` | Создать вложение |
|
||||||
|
| DELETE | `/gateway/api/v1/attachments/{id}` | `store/resource.ts` | Удалить вложение |
|
||||||
|
|
||||||
|
### `sarex` — Локальный сервис данных
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/client/settings/` | `store/inspections.ts` | Клиентские настройки |
|
||||||
|
| GET | `/api/core/users/` | `store/inspections.ts` | Пользователи |
|
||||||
|
| GET | `/api/core/admin/departments/?company={companyId}` | `store/inspections.ts` | Отделы компании |
|
||||||
|
| GET | `/api/core/admin/positions/?company={companyId}` | `store/inspections.ts` | Должности компании |
|
||||||
|
|
||||||
|
### `eavV4` — Сервис атрибутов EAV (ассеты)
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/assets/search/` | `api/assets.ts` | Поиск ассетов по списку id |
|
||||||
|
| GET | `/assets/` | `api/assets.ts` | Список ассетов (фильтры/пагинация/теги в query) |
|
||||||
|
|
||||||
|
> Теги ассетов (`EnumAssetTags`): `location`, `project_structure`, `events.can_be_selected`, `remarks.can_be_selected`.
|
||||||
|
|
||||||
|
### `eavV0` — Сервис атрибутов EAV (атрибуты)
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/attribute/?company_id={companyId}` | `store/inspections.ts` | Атрибуты компании |
|
||||||
|
|
||||||
|
### `issues` — Сервис замечаний
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| POST | `/issues/` | `api/Issues.ts` | Создать замечание |
|
||||||
|
| GET | `/issues/` | `api/Issues.ts` | Список замечаний (фильтры по компании/ресурсу/событию/типам) |
|
||||||
|
| POST | `/attachments/` | `api/Issues.ts`, `store/issuesStore.ts` | Прикрепить медиа к замечанию |
|
||||||
|
| GET | `/companies/{companyId}/status-model/` | `api/Issues.ts` | Статусная модель замечаний компании |
|
||||||
|
| GET | `/issue-types/` | `api/Issues.ts` | Типы замечаний компании |
|
||||||
|
|
||||||
|
### `premises` — Сервис помещений
|
||||||
|
|
||||||
|
Базовый хост уже включает `/api/v1`.
|
||||||
|
|
||||||
|
| Метод | Путь | Где вызывается | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/premises/{id}/` | `api/premises-api.ts` | Помещение по id |
|
||||||
|
| POST | `/premises/filter/` | `api/premises-api.ts` | Помещения по фильтру (пагинация в query) |
|
||||||
|
| POST | `/premise_types/filter/` | `api/premises-api.ts` | Типы помещений по фильтру (пагинация в query) |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Показ уведомлений об ошибках управляется флагом `showErrorNotification` в параметрах запроса (включается точечно для части запросов). Для сериализации query-параметров-массивов местами используется `query-string` с `arrayFormat: "comma"` (напр. в `api/Issues.ts`). Часть запросов в `api/assets.ts` оборачивает ошибку в `throw new Error(...)`.
|
||||||
1243
apps/inspections/openapi.yaml
Normal file
1243
apps/inspections/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
101
apps/issues/.env.example
Normal file
101
apps/issues/.env.example
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# Django
|
||||||
|
DJANGO_SETTINGS_MODULE=config.settings.production
|
||||||
|
DJANGO_ADMIN_SECRET_KEY=''
|
||||||
|
DJANGO_TOKEN=django-token
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
ENVIRONMENT=production
|
||||||
|
ENVIRONMENT_CLIENT=stage
|
||||||
|
|
||||||
|
# Database (PostgreSQL)
|
||||||
|
DATABASE_NAME=postgres
|
||||||
|
DATABASE_USER=postgres
|
||||||
|
DATABASE_PASSWORD=password
|
||||||
|
DATABASE_HOST=127.0.0.1
|
||||||
|
DATABASE_PORT=5432
|
||||||
|
|
||||||
|
# Sarex auth (basic)
|
||||||
|
SAREX_USERNAME=
|
||||||
|
SAREX_PASSWORD=
|
||||||
|
|
||||||
|
# External services
|
||||||
|
AERO_HOST=https://stage.sarex.io
|
||||||
|
AERO_PUBLIC_HOST=https://stage.sarex.io
|
||||||
|
BASE_AERO_URL=https://stage.sarex.io
|
||||||
|
BASE_AUTH_URL=https://stage.sarex.io
|
||||||
|
SAREX_API=https://stage.sarex.io
|
||||||
|
SAREX_HOST=https://stage.sarex.io
|
||||||
|
SERVICE_URL=https://stage.sarex.io
|
||||||
|
GATEWAY_URL=https://stage-api.sarex.io/gateway
|
||||||
|
DOCUMENTATIONS_URL=http://documentations-api-svc.documentations.svc.cluster.local:8000
|
||||||
|
WORKFLOWS_URL=http://workflows-api-service.platform.svc.cluster.local:8000
|
||||||
|
WORKFLOWS_HOST=http://workflows-api-service.platform.svc.cluster.local:8000
|
||||||
|
RESOURCES_API_HOST=http://iams.platform.svc.cluster.local:8080
|
||||||
|
REVIEW_HOST=https://stage-api.sarex.io/flows
|
||||||
|
INSPECTION_HOST=https://stage-api.sarex.io/inspections
|
||||||
|
EAV_HOST=http://eav-service.eav-stage
|
||||||
|
|
||||||
|
# RabbitMQ / Celery broker
|
||||||
|
RABBITMQ_USERNAME=mcc
|
||||||
|
RABBITMQ_PASSWORD=mcc
|
||||||
|
RABBITMQ_HOSTNAME=rabbitmq-service
|
||||||
|
RABBITMQ_VHOST=api
|
||||||
|
|
||||||
|
# Redis (Celery result backend)
|
||||||
|
REDIS_HOST=redis
|
||||||
|
REDIS_DB=0
|
||||||
|
|
||||||
|
# Kafka
|
||||||
|
KAFKA_HOST=
|
||||||
|
KAFKA_USERNAME=
|
||||||
|
KAFKA_PASSWORD=
|
||||||
|
KAFKA_SSL_CAFILE=/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.crt
|
||||||
|
KAFKA_EAV_ASSETS_TOPIC=assets-broadcast-test
|
||||||
|
KAFKA_ISSUES_TOPIC=issues-broadcast
|
||||||
|
|
||||||
|
# S3 (Yandex Cloud) — общий бакет
|
||||||
|
YC_S3_ACCESS_KEY_ID=
|
||||||
|
YC_S3_SECRET_ACCESS_KEY=
|
||||||
|
YC_S3_BUCKET_NAME=
|
||||||
|
YC_S3_ENDPOINT_URL=
|
||||||
|
YC_S3_VERIFY=true
|
||||||
|
|
||||||
|
# S3 — бакет предписаний
|
||||||
|
PRESCRIPTION_S3_ACCESS_KEY_ID=
|
||||||
|
PRESCRIPTION_S3_SECRET_ACCESS_KEY=
|
||||||
|
PRESCRIPTION_S3_BUCKET=
|
||||||
|
PRESCRIPTION_S3_ENDPOINT_URL=
|
||||||
|
|
||||||
|
# Email
|
||||||
|
ENABLE_MAILGUN=True
|
||||||
|
EMAIL_FROM=hello@sarex.io
|
||||||
|
EMAIL_DOCKER_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/notification:email
|
||||||
|
MAILGUN_BASE_URL=https://api.mailgun.net/v3/mg.sarex.io
|
||||||
|
MAILGUN_API_KEY=
|
||||||
|
USE_NOTIFICATIONS=True
|
||||||
|
# SMTP (альтернатива Mailgun)
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=
|
||||||
|
|
||||||
|
# Prescriptions workflow
|
||||||
|
PRESCRIPTION_WF_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/rendering-template:develop
|
||||||
|
PRESCRIPTION_WF_RESULT_PATH=prescriptions-storage-stage
|
||||||
|
PRESCRIPTION_WF_CALLBACK=cr.yandex/crp3ccidau046kdj8g9q/webhook-caller:develop
|
||||||
|
PRESCRIPTION_INTERNAL_HOST=http://issues-backend-service.proc.svc.cluster.local:80/internal
|
||||||
|
DOCX_TO_PDF_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/docx-to-pdf:latest
|
||||||
|
|
||||||
|
# Export
|
||||||
|
EXPORT_WF_CROPPING_DOCKER_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/crop-issue-pin-area:stage
|
||||||
|
|
||||||
|
# OpenTelemetry
|
||||||
|
USE_OTEL=False
|
||||||
|
SERVICE_NAME=issues-backend.sarex-issues
|
||||||
|
TRACER_ENDPOINT=localhost:4375
|
||||||
|
USE_INSECURE=True
|
||||||
|
MODULE=issues
|
||||||
|
TEAM=proc_team
|
||||||
|
COMPONENT=backend
|
||||||
|
|
||||||
|
# uWSGI / infra
|
||||||
|
API_ADDRESS=8000
|
||||||
|
SENTRY_KEY=
|
||||||
245
apps/issues/CONFIGURATION.md
Normal file
245
apps/issues/CONFIGURATION.md
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# Конфигурация проекта issues-backend
|
||||||
|
|
||||||
|
Документ описывает все переменные окружения и способы конфигурирования сервиса замечаний (Issues).
|
||||||
|
|
||||||
|
## Способы конфигурирования
|
||||||
|
|
||||||
|
Сервис настраивается **через переменные окружения**. Это Django-приложение; настройки читаются в `src/config/settings/base.py` и `src/config/settings/production.py` напрямую через `os.getenv(...)`. В начале `base.py` вызывается `load_dotenv()` ([`python-dotenv`](https://pypi.org/project/python-dotenv/)), поэтому при локальном запуске файл `.env` из рабочего каталога **подхватывается автоматически**.
|
||||||
|
|
||||||
|
Активный модуль настроек задаётся переменной `DJANGO_SETTINGS_MODULE` (в контейнере/Helm — `config.settings.production`) либо флагом `--settings=config.settings.production` у `manage.py`. Модуль `production.py` импортирует всё из `base.py` и переопределяет `DEBUG=False`, `ALLOWED_HOSTS`, `SIMPLE_JWT`, `LOGGING` и часть внешних хостов.
|
||||||
|
|
||||||
|
Отдельного конфиг-файла (yaml/toml) у приложения нет. Дополнительно на инфраструктурном уровне используются `config/settings/base.py` для Celery/Kafka/OTel и Helm-чарт для задания переменных в Kubernetes.
|
||||||
|
|
||||||
|
Источники переменных по способам запуска:
|
||||||
|
|
||||||
|
| Способ запуска | Откуда берутся переменные |
|
||||||
|
| --- | --- |
|
||||||
|
| Локально | Переменные окружения процесса и файл `.env` (грузится `load_dotenv()` в `base.py`) |
|
||||||
|
| Локально (Kafka) | `docker compose --file local-kafka-docker-compose.yml up -d` поднимает брокер; консьюмер — `manage.py consume_kafka` |
|
||||||
|
| Kubernetes (Helm) | `.helm/values.yaml`: блоки `envs` (обычные значения) и `secretEnvs` (значения из k8s-секретов) для каждого сервиса (`api`, `celery`, `celery-beat`, `kafka-app`) |
|
||||||
|
| CI/CD (GitLab) | `.gitlab-ci.yml`: общий шаблон `generic/common-ci` (`universal-pipeline.yaml`, ref `apps-business`); окружение выбирается по ветке/тегу |
|
||||||
|
|
||||||
|
Способы запуска процессов:
|
||||||
|
|
||||||
|
| Процесс | Команда | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| HTTP API | `uwsgi` (entrypoint) / `manage.py runserver` | REST API (DRF), OpenAPI-схема через drf-spectacular |
|
||||||
|
| Celery worker | `celery -A config worker -l info -E --concurrency=2` | Обработчик фоновых задач (`issues.tasks`, `issues.notifications`, `prescriptions.tasks`) |
|
||||||
|
| Celery beat | `celery -A config beat -l info` | Периодические задачи (ежедневный инкремент счётчиков, отчёт о просрочках) |
|
||||||
|
| Kafka consumer | `python3 run_kafka_app.py` / `manage.py consume_kafka` | Консьюмер Kafka (топики ассетов и замечаний) |
|
||||||
|
|
||||||
|
Порядок старта в контейнере задаётся `compose/server/entrypoint.sh` (миграции + запуск uWSGI по `compose/server/uwsgi.ini`). Базовый образ — `python:3.10-slim-bookworm` (`compose/server/Dockerfile`).
|
||||||
|
|
||||||
|
## Переменные приложения
|
||||||
|
|
||||||
|
Дефолт `—` означает, что явного значения по умолчанию в коде нет (`os.getenv` вернёт `None`); для корректной работы переменную нужно задать.
|
||||||
|
|
||||||
|
### Django и окружение
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DJANGO_SETTINGS_MODULE` | string | `config.settings.production` (Helm) | Модуль настроек Django |
|
||||||
|
| `DJANGO_ADMIN_SECRET_KEY` | string | `''` | `SECRET_KEY` Django |
|
||||||
|
| `DJANGO_TOKEN` | string | `django-token` | Служебный токен |
|
||||||
|
| `ENVIRONMENT` | string | `production` | Окружение развёртывания (в т.ч. атрибут OTel) |
|
||||||
|
| `ENVIRONMENT_CLIENT` | string | `production` | Клиентское окружение (`stage`/`preprod`/`production`) |
|
||||||
|
|
||||||
|
### База данных (PostgreSQL)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DATABASE_NAME` | string | — | Имя базы данных |
|
||||||
|
| `DATABASE_USER` | string | — | Пользователь БД |
|
||||||
|
| `DATABASE_PASSWORD` | string | — | Пароль пользователя БД |
|
||||||
|
| `DATABASE_HOST` | string | — | Хост PostgreSQL |
|
||||||
|
| `DATABASE_PORT` | int | — | Порт PostgreSQL |
|
||||||
|
|
||||||
|
> Движок — `django.db.backends.postgresql`. В Kubernetes значения приходят из секрета (`issues-postgresql-secret` для stage, `ya-pg-secret` для preprod/production), CA-сертификат монтируется как `/root/.postgresql/ca.crt`.
|
||||||
|
|
||||||
|
### Внешние сервисы (URL)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `SAREX_API` | string | — | Базовый API Sarex (`SAREX_HOST` по умолчанию равен ему) |
|
||||||
|
| `SAREX_HOST` | string | `= SAREX_API` | Хост Sarex |
|
||||||
|
| `AERO_HOST` | string | `https://stage.sarex.io` | Хост Aero |
|
||||||
|
| `AERO_PUBLIC_HOST` | string | `https://stage.sarex.io` (в `production.py` — из env) | Публичный хост Aero |
|
||||||
|
| `BASE_AERO_URL` | string | `https://lk.sarex.io` | Базовый URL Aero |
|
||||||
|
| `BASE_AUTH_URL` | string | `https://lk.sarex.io` | Базовый URL аутентификации |
|
||||||
|
| `SERVICE_URL` | string | `https://lk.sarex.io` | URL сервиса |
|
||||||
|
| `GATEWAY_URL` | string | `https://lk.sarex.io` | URL gateway |
|
||||||
|
| `DOCUMENTATIONS_URL` | string | `https://lk.sarex.io` | URL сервиса документаций |
|
||||||
|
| `WORKFLOWS_URL` | string | `https://lk.sarex.io` | URL сервиса workflows |
|
||||||
|
| `WORKFLOWS_HOST` | string | `https://lk.sarex.io` | Хост workflows |
|
||||||
|
| `RESOURCES_API_HOST` | string | `https://lk.sarex.io` (в `production.py` — `http://sarex-resources-service.resources-prod`) | Хост сервиса ресурсов (IAM) |
|
||||||
|
| `REVIEW_HOST` | string | `https://lk.sarex.io` | Хост сервиса review/flows |
|
||||||
|
| `INSPECTION_HOST` | string | `https://lk.sarex.io` | Хост сервиса инспекций |
|
||||||
|
| `EAV_HOST` | string | `http://eav-service.eav-stage` | Хост сервиса атрибутов (EAV) |
|
||||||
|
| `SAREX_USERNAME` | string | — | Логин для basic-auth Sarex |
|
||||||
|
| `SAREX_PASSWORD` | string | — | Пароль для basic-auth Sarex |
|
||||||
|
|
||||||
|
### RabbitMQ и Celery
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `RABBITMQ_USERNAME` | string | `mcc` | Пользователь брокера |
|
||||||
|
| `RABBITMQ_PASSWORD` | string | `mcc` | Пароль брокера |
|
||||||
|
| `RABBITMQ_HOSTNAME` | string | `rabbitmq-service` | Хост брокера |
|
||||||
|
| `RABBITMQ_VHOST` | string | `api` | Виртуальный хост |
|
||||||
|
| `REDIS_HOST` | string | `redis` | Хост Redis (result backend) |
|
||||||
|
| `REDIS_DB` | int | `0` | Номер БД Redis |
|
||||||
|
|
||||||
|
> `CELERY_BROKER_URL` собирается как `amqp://{user}:{password}@{hostname}/{vhost}` + `?heartbeat=30`. `CELERY_RESULT_BACKEND` — `redis://{REDIS_HOST}:6379/{REDIS_DB}`. Расписание beat: инкремент счётчиков `1:00`, отчёт о просрочках `6:00` (`Europe/Moscow`).
|
||||||
|
|
||||||
|
### Kafka
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `KAFKA_HOST` | string | — | Адрес брокера Kafka |
|
||||||
|
| `KAFKA_USERNAME` | string | — | Пользователь |
|
||||||
|
| `KAFKA_PASSWORD` | string | — | Пароль |
|
||||||
|
| `KAFKA_SSL_CAFILE` | string | — | Путь к CA-сертификату (в Helm — YandexInternalRootCA) |
|
||||||
|
| `KAFKA_EAV_ASSETS_TOPIC` | string | — | Топик трансляции ассетов (EAV) |
|
||||||
|
| `KAFKA_ISSUES_TOPIC` | string | — | Топик трансляции замечаний |
|
||||||
|
|
||||||
|
### S3 (Yandex Cloud)
|
||||||
|
|
||||||
|
Основное хранилище (`django-storages`, `S3Boto3Storage`):
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `YC_S3_ACCESS_KEY_ID` | string | — | Access key |
|
||||||
|
| `YC_S3_SECRET_ACCESS_KEY` | string | — | Secret key |
|
||||||
|
| `YC_S3_BUCKET_NAME` | string | — | Имя бакета |
|
||||||
|
| `YC_S3_ENDPOINT_URL` | string | — | Эндпоинт S3 |
|
||||||
|
| `YC_S3_VERIFY` | bool | `None` | Проверять TLS-сертификат (`"true"` → `True`) |
|
||||||
|
|
||||||
|
Хранилище предписаний (отдельный бакет):
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `PRESCRIPTION_S3_ACCESS_KEY_ID` | string | — | Access key |
|
||||||
|
| `PRESCRIPTION_S3_SECRET_ACCESS_KEY` | string | — | Secret key |
|
||||||
|
| `PRESCRIPTION_S3_BUCKET` | string | — | Имя бакета |
|
||||||
|
| `PRESCRIPTION_S3_ENDPOINT_URL` | string | — | Эндпоинт S3 |
|
||||||
|
|
||||||
|
### Почта (Mailgun / SMTP)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `ENABLE_MAILGUN` | bool | `True` | Использовать Mailgun |
|
||||||
|
| `MAILGUN_BASE_URL` | string | `https://api.mailgun.net/v3/mg.sarex.io` | URL API Mailgun |
|
||||||
|
| `MAILGUN_API_KEY` | string | (задан дефолт в коде) | API-ключ Mailgun (в проде — из секрета) |
|
||||||
|
| `EMAIL_FROM` | string | `hello@sarex.io` | Адрес отправителя |
|
||||||
|
| `EMAIL_DOCKER_IMAGE` | string | `cr.yandex/.../notification:email` | Образ сервиса нотификаций |
|
||||||
|
| `USE_NOTIFICATIONS` | bool | `True` | Включить отправку уведомлений (`False`/`false`/`0` → выкл.) |
|
||||||
|
| `SMTP_HOST` | string | `None` (в `production.py` — `""`) | SMTP-хост (альтернатива Mailgun) |
|
||||||
|
| `SMTP_PORT` | int | `None` | SMTP-порт |
|
||||||
|
|
||||||
|
### Предписания (workflow)
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `PRESCRIPTION_WF_IMAGE` | string | — | Образ workflow генерации предписаний |
|
||||||
|
| `DOCX_TO_PDF_IMAGE` | string | — | Образ конвертера DOCX→PDF |
|
||||||
|
| `PRESCRIPTION_WF_RESULT_PATH` | string | — | Путь/бакет результата |
|
||||||
|
| `PRESCRIPTION_WF_CALLBACK` | string | — | Образ webhook-caller |
|
||||||
|
| `PRESCRIPTION_INTERNAL_HOST` | string | — | Внутренний хост колбэков предписаний |
|
||||||
|
| `EXPORT_WF_CROPPING_DOCKER_IMAGE` | string | `cr.yandex/.../crop-issue-pin-area:prod` | Образ кропа области пина для экспорта |
|
||||||
|
|
||||||
|
### OpenTelemetry
|
||||||
|
|
||||||
|
Трейсинг подключается только если `USE_OTEL` истинно (`django_otel_tools`).
|
||||||
|
|
||||||
|
| Переменная | Тип | Значение по умолчанию | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `USE_OTEL` | bool | `False` | Включить трейсинг/логирование через OTel |
|
||||||
|
| `SERVICE_NAME` | string | `issues-backend.sarex-issues` | Имя сервиса в трейсах |
|
||||||
|
| `TRACER_ENDPOINT` | string | `localhost:4375` | Адрес OTLP-коллектора |
|
||||||
|
| `USE_INSECURE` | bool | `False` | Небезопасное (без TLS) подключение к коллектору |
|
||||||
|
| `MODULE` | string | `issues` | Атрибут трейсов |
|
||||||
|
| `TEAM` | string | `proc_team` | Атрибут трейсов |
|
||||||
|
| `COMPONENT` | string | `backend` | Атрибут трейсов |
|
||||||
|
|
||||||
|
## Переменные инфраструктуры
|
||||||
|
|
||||||
|
Не читаются кодом приложения напрямую (или используются вспомогательными компонентами), но участвуют в запуске/деплое.
|
||||||
|
|
||||||
|
| Переменная | Где используется | Назначение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `API_ADDRESS` | Helm (`envs`) | Порт uWSGI (`8000`) |
|
||||||
|
| `SENTRY_KEY` | Helm (`envs`) | DSN Sentry (задан для stage) |
|
||||||
|
| `SAREX_MAILER_URL` | Helm (`envs`) | URL сервиса рассылок (`http://mailer-service.mailer:8000`) |
|
||||||
|
| `MAILGUN_HOST` | Helm (`envs`) | Хост Mailgun на уровне чарта |
|
||||||
|
| `NPM_TOKEN`, `BUILD_ENV` | CI/Dockerfile | Сборка (актуально для фронтенда) |
|
||||||
|
|
||||||
|
## Переменные из Helm-чарта (`.helm/values.yaml`)
|
||||||
|
|
||||||
|
Чарт — обёртка над `universal-chart`. Определены четыре сервиса: `api`, `celery`, `celery-beat`, `kafka-app`. Обычные значения (`envs`) задаются для окружений `_default`/`stage`/`preprod`/`production` (различаются адресами БД/сервисов, топиками Kafka, образами, `SERVICE_NAME`, `TRACER_ENDPOINT`).
|
||||||
|
|
||||||
|
Значения из секретов (блок `secretEnvs`, монтируются через `secretKeyRef`):
|
||||||
|
|
||||||
|
| Переменная | Секрет (stage / preprod-prod) | Ключ |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `KAFKA_USERNAME` | `issues-kafka-secret` / `yc-kafka-secret` | `username` |
|
||||||
|
| `KAFKA_PASSWORD` | `issues-kafka-secret` / `yc-kafka-secret` | `password` |
|
||||||
|
| `KAFKA_HOST` | `issues-kafka-secret` / `yc-kafka-secret` | `host` |
|
||||||
|
| `SAREX_USERNAME` | `sarex-auth` | `username` |
|
||||||
|
| `SAREX_PASSWORD` | `sarex-auth` | `password` |
|
||||||
|
| `DATABASE_HOST` | `issues-postgresql-secret` / `ya-pg-secret` | `host` |
|
||||||
|
| `DATABASE_NAME` | `issues-postgresql-secret` / `ya-pg-secret` | `database` |
|
||||||
|
| `DATABASE_PORT` | `issues-postgresql-secret` / `ya-pg-secret` | `port` |
|
||||||
|
| `DATABASE_USER` | `issues-postgresql-secret` / `ya-pg-secret` | `username` |
|
||||||
|
| `DATABASE_PASSWORD` | `issues-postgresql-secret` / `ya-pg-secret` | `password` |
|
||||||
|
| `YC_S3_ACCESS_KEY_ID` | `issues-s3-secret` / `yc-s3-secret` | `key_id` |
|
||||||
|
| `YC_S3_SECRET_ACCESS_KEY` | `issues-s3-secret` / `yc-s3-secret` | `access_key` |
|
||||||
|
| `YC_S3_BUCKET_NAME` | `issues-s3-secret` / `yc-s3-secret` | `storage_bucket_name` |
|
||||||
|
| `YC_S3_ENDPOINT_URL` | `issues-s3-secret` / `yc-s3-secret` | `endpoint_url` |
|
||||||
|
| `RABBITMQ_VHOST` | `issues-rabbitmq-secret` / `rabbitmq-secret` | `vhost` |
|
||||||
|
| `RABBITMQ_USERNAME` | `issues-rabbitmq-secret` / `rabbitmq-secret` | `user` |
|
||||||
|
| `RABBITMQ_HOSTNAME` | `issues-rabbitmq-secret` / `rabbitmq-secret` | `host` |
|
||||||
|
| `RABBITMQ_PASSWORD` | `issues-rabbitmq-secret` / `rabbitmq-secret` | `password` |
|
||||||
|
| `MAILGUN_API_KEY` | `mailgun-secret` | `api-key` |
|
||||||
|
| `DJANGO_TOKEN` | `django-secret` | `token` |
|
||||||
|
| `DJANGO_ADMIN_SECRET_KEY` | `django-admin-secret` | `secret_key` |
|
||||||
|
| `PRESCRIPTION_S3_ACCESS_KEY_ID` | `prescription-s3-secret` | `key_id` |
|
||||||
|
| `PRESCRIPTION_S3_SECRET_ACCESS_KEY` | `prescription-s3-secret` | `access_key` |
|
||||||
|
| `PRESCRIPTION_S3_BUCKET` | `prescription-s3-secret` | `storage_bucket_name` |
|
||||||
|
| `PRESCRIPTION_S3_ENDPOINT_URL` | `prescription-s3-secret` | `endpoint_url` |
|
||||||
|
|
||||||
|
Дополнительно чарт монтирует конфиг uWSGI (`uwsgi-configmap` → `/opt/server/uwsgi.ini`, только сервис `api`), CA-сертификат PostgreSQL (`yc-ch-certificate` → `/root/.postgresql/ca.crt`) и внутренний CA Яндекса (`YandexInternalRootCA.crt`).
|
||||||
|
|
||||||
|
## Переменные в CI (`.gitlab-ci.yml`)
|
||||||
|
|
||||||
|
Пайплайн подключает общие шаблоны из `generic/common-ci` (`common-security-scan.yaml`, `universal-pipeline.yaml`, ref `apps-business`). Основные переменные: `SERVICE_NAME=issues`, `DOCKERFILE_PATH=./compose/server/Dockerfile`. Окружение выбирается по ветке/тегу:
|
||||||
|
|
||||||
|
| Условие | STAND | Namespace | CHART_VERSION |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ветка `stage` | `stage` | `proc` | `0.0.1-stage` |
|
||||||
|
| ветка `master` | `preprod` | `issues-preprod` | `0.0.1-preprod` |
|
||||||
|
| тег (`CI_COMMIT_TAG`) | `production` | `issues-prod` | `0.0.1-prod` |
|
||||||
|
|
||||||
|
`HELM_SET_ARGS` для каждого окружения проставляет образы четырёх сервисов (`api`, `celery`, `celery-beat`, `kafka-app`), `universal-chart.global.env` и метаданные коммита (`commitSha`, `gitlabUri`, `gitlabJobUrl`, `owner`).
|
||||||
|
|
||||||
|
## Замечания и потенциальные проблемы
|
||||||
|
|
||||||
|
- В отличие от FastAPI-сервисов, переменные не имеют единого префикса и читаются напрямую через `os.getenv`. Файл `.env` подхватывается автоматически (`load_dotenv()` в `base.py`).
|
||||||
|
- `DEBUG` в `base.py` установлен в `True`; в `production.py` переопределяется на `False`. Для боевого окружения обязателен модуль `config.settings.production`.
|
||||||
|
- `MAILGUN_API_KEY` имеет захардкоженный дефолт в коде — в реальных окружениях его нужно переопределять секретом.
|
||||||
|
- Ряд переменных без дефолта (`DATABASE_*`, `KAFKA_*`, `YC_S3_*`, `SAREX_USERNAME`/`SAREX_PASSWORD`, `PRESCRIPTION_WF_*`) обязательны для полноценной работы соответствующих подсистем.
|
||||||
|
- Переменные `SAREX_MAILER_URL`, `MAILGUN_HOST`, `SENTRY_KEY`, `API_ADDRESS` задаются в Helm, но не читаются кодом приложения напрямую.
|
||||||
|
|
||||||
|
## Минимальный набор для локального запуска
|
||||||
|
|
||||||
|
Postgres, RabbitMQ, Redis и Kafka поднимаются локально; приложение — `python ./src/manage.py runserver --settings=config.settings.production`, консьюмер — `manage.py consume_kafka`. Минимально необходимо задать:
|
||||||
|
|
||||||
|
- `DJANGO_ADMIN_SECRET_KEY`
|
||||||
|
- `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`, `DATABASE_HOST`, `DATABASE_PORT`
|
||||||
|
- `RABBITMQ_USERNAME`, `RABBITMQ_PASSWORD`, `RABBITMQ_HOSTNAME`, `RABBITMQ_VHOST`, `REDIS_HOST`
|
||||||
|
- `KAFKA_HOST`, `KAFKA_USERNAME`, `KAFKA_PASSWORD`, `KAFKA_EAV_ASSETS_TOPIC`, `KAFKA_ISSUES_TOPIC` (для консьюмера)
|
||||||
|
- `YC_S3_*` (для работы с файлами) и при необходимости `PRESCRIPTION_S3_*`
|
||||||
|
- внешние URL: `SAREX_API`, `AERO_HOST`, `GATEWAY_URL`, `DOCUMENTATIONS_URL`, `WORKFLOWS_URL`, `RESOURCES_API_HOST`, `EAV_HOST`, `INSPECTION_HOST`, `REVIEW_HOST`
|
||||||
|
- почта: `ENABLE_MAILGUN` + `MAILGUN_*` **или** `SMTP_HOST`/`SMTP_PORT`
|
||||||
|
- `USE_OTEL=False` для локальной разработки
|
||||||
|
|
||||||
|
Готовые значения-примеры приведены в `.env.example`.
|
||||||
138
apps/issues/ENDPOINTS.md
Normal file
138
apps/issues/ENDPOINTS.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Эндпоинты, с которыми взаимодействует issues-frontend
|
||||||
|
|
||||||
|
Документ описывает все HTTP-эндпоинты внешних сервисов, к которым обращается модуль (микрофронтенд `issues-frontend`).
|
||||||
|
|
||||||
|
## Как устроено взаимодействие
|
||||||
|
|
||||||
|
Запросы сгруппированы по доменным API-объектам в каталоге `module/api/` (`IssuesApi`, `CoreApi`, `PrescriptionsApi`, `InspectionsApi`, `AttributesApi`, `AssetsApi`, `ContractsApi`, `ResourcesApi`, `TemplatesApi`, `PremisesApi`, `DocumentationApi`). Каждый метод вызывает единый `httpService` (`module/api/http-service.ts`).
|
||||||
|
|
||||||
|
`httpService` создаётся фабрикой `createHttpService` из `@sarex-team/sdk-js` и принимает карту хостов `hosts` (`module/api/hosts.ts`) и текущее окружение `BUILD_ENV`. Вызов задаётся объектом:
|
||||||
|
|
||||||
|
- `service` — логическое имя сервиса (ключ из `hosts`, см. таблицу ниже);
|
||||||
|
- метод — `getRequest`/`postRequest`/`putRequest`/`patchRequest`/`deleteRequest`;
|
||||||
|
- `url` — путь запроса (дописывается к базовому хосту сервиса);
|
||||||
|
- `axiosConfig` — доп. настройки axios (`params`, `paramsSerializer`, `responseType` и т.п.);
|
||||||
|
- `data` — тело запроса;
|
||||||
|
- `showErrorNotification`, `queryKey`, `cache` — опции показа ошибок, ключа кеша и кеширования.
|
||||||
|
|
||||||
|
Итоговый URL = `<базовый хост сервиса для BUILD_ENV>` + `url`. Базовый хост выбирается по `BUILD_ENV` (`local`/`stage`/`prod`/`preprod`/`contour`).
|
||||||
|
|
||||||
|
## Базовые хосты по сервисам и окружениям
|
||||||
|
|
||||||
|
Значения из `module/api/hosts.ts`. Приведены `stage` и `prod`; в `contour` используются относительные пути, в `preprod` — домен `api.preprod.sarex.io`, в `local` — как в `stage`, но `sarex` проксируется на `/sarex-backend`.
|
||||||
|
|
||||||
|
| Сервис (`service`) | Назначение | `stage` | `prod` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `issues` | Сервис замечаний (issues-backend, собственный API) | `https://stage-api.sarex.io/issues/api` | `https://api.sarex.io/issues/api` |
|
||||||
|
| `prescriptions` | Предписания (issues-backend) | `https://stage-api.sarex.io/issues/api/prescriptions` | `https://api.sarex.io/issues/api/prescriptions` |
|
||||||
|
| `sarexApi` | Gateway/API Sarex (`/gateway`, `/issues`, `/inspections`, `/contracts`) | `https://stage-api.sarex.io` | `https://api.sarex.io` |
|
||||||
|
| `sarex` | Локальный сервис данных (`/api/core`, `/api/client`) | `""` (относительные пути) | `""` |
|
||||||
|
| `gateway` | Gateway Sarex | `https://stage-api.sarex.io/gateway` | `https://api.sarex.io/gateway` |
|
||||||
|
| `documentations` | Сервис документации | `https://stage-api.sarex.io/documentations/api/v1` | `https://api.sarex.io/documentations/api/v1` |
|
||||||
|
| `eav` | Сервис атрибутов/ассетов (EAV) | `https://stage-api.sarex.io/eav/api` | `https://api.sarex.io/eav/api` |
|
||||||
|
| `files` | Сервис файлов | `https://stage-api.sarex.io/files/api/v1` | `https://api.sarex.io/files/api/v1` |
|
||||||
|
| `premises` | Сервис помещений | `https://stage-api.sarex.io/premises/api/v1` | `https://api.sarex.io/premises/api/v1` |
|
||||||
|
| `workspaces` | Сервис рабочих областей | `https://stage-api.sarex.io/workspaces` | `https://api.sarex.io/workspaces` |
|
||||||
|
| `workflows` | Сервис обработки документов | `https://stage-api.sarex.io/workflows` | `https://api.sarex.io/workflows` |
|
||||||
|
| `comparisons` | Сервис сравнений | `https://stage-api.sarex.io/comparisons` | `https://api.sarex.io/comparisons` |
|
||||||
|
| `remarks` | Сервис замечаний (remarks) | `https://stage-api.sarex.io/remarks` | `https://api.sarex.io/remarks` |
|
||||||
|
| `bim` / `bimv2` | BIM-API | `https://stage-api.sarex.io/bim` (`/bimv2`) | `https://api.sarex.io/bim` (`/bimv2`) |
|
||||||
|
| `google` | Временное хранилище (GCS) | `https://storage.googleapis.com/srx-tmp` | `https://storage.googleapis.com/srx-tmp` |
|
||||||
|
| `zitadel` | IdP (аутентификация) | `https://idp.dev.stage.sarex.io` | `https://login.sarex.io` |
|
||||||
|
|
||||||
|
> Часть сервисов объявлена в карте хостов, но напрямую в `module/api/*` не вызывается (`workspaces`, `workflows`, `comparisons`, `remarks`, `bim`, `bimv2`, `google`, `zitadel`) — они используются инфраструктурой SDK / другими слоями. Сервис `prescriptions` объявлен в хостах, но методы предписаний фактически ходят через `sarexApi` по пути `/issues/api/prescriptions`.
|
||||||
|
|
||||||
|
Подключаемый удалённый модуль `documentations` описан отдельно в `module/api/module-hosts.ts` (`remoteEntry.js` микрофронтенда documentations).
|
||||||
|
|
||||||
|
## Эндпоинты по сервисам
|
||||||
|
|
||||||
|
### `issues` — Сервис замечаний (собственный API)
|
||||||
|
|
||||||
|
Файл `module/api/issuesApi.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `IssuesApi.getStatusModels` | GET | `/companies/{companyId}/status-model/` | Модель статусов компании (по `resource_id`, `issue_type_id`) |
|
||||||
|
| `IssuesApi.getTypes` | GET | `/issue-types/` | Типы замечаний компании (`company_id`) |
|
||||||
|
| `IssuesApi.getIssue` | GET | `/issues/{public_id}/` | Замечание по публичному id |
|
||||||
|
| `IssuesApi.editIssue` | PATCH | `/issues/{publicId}/` | Редактировать замечание |
|
||||||
|
| `IssuesApi.getChanges` | GET | `/issue-changes/` | История изменений замечания (`issue_id`) |
|
||||||
|
|
||||||
|
### `sarexApi` — Gateway/API Sarex
|
||||||
|
|
||||||
|
Файлы `issuesApi.ts`, `prescriptionsApi.ts`, `inspections.ts`, `contractsApi.ts`, `attributes.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `IssuesApi.postComment` | POST | `/issues/api/comments/` | Добавить комментарии |
|
||||||
|
| `IssuesApi.deleteComment` | DELETE | `/issues/api/comments/{id}/` | Удалить комментарий |
|
||||||
|
| `IssuesApi.postAttachment` | POST | `/issues/api/attachments/` | Загрузить вложение (multipart) |
|
||||||
|
| `IssuesApi.deleteAttachment` | DELETE | `/issues/api/attachments/{id}/` | Удалить вложение (`issue_public_id`) |
|
||||||
|
| `PrescriptionsApi.postPrescription` | POST | `/issues/api/prescriptions/` | Создать предписание |
|
||||||
|
| `PrescriptionsApi.getPrescriptions` | GET | `/issues/api/prescriptions/` | Список предписаний (сериализованные фильтры в query) |
|
||||||
|
| `InspectionsApi.getInspections` | GET | `/inspections/api/v1/inspections/light/` | Список инспекций (`company_id`, `limit`, `offset`) |
|
||||||
|
| `InspectionsApi.getInspectionTypes` | GET | `/inspections/api/v1/inspections/types/` | Типы инспекций (`company_id`) |
|
||||||
|
| `ContractsApi.getContracts` | GET | `/contracts/api/v0/contracts/` | Договоры (`tenant_id`, `contractor_id`, `resource_id`) |
|
||||||
|
| `AttributesApi.getDocumentAttributes` | GET | `/gateway/api/v1/documents/{documentId}/attributes/` | Атрибуты документа |
|
||||||
|
|
||||||
|
### `sarex` — Локальный сервис данных
|
||||||
|
|
||||||
|
Файл `module/api/coreApi.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `CoreApi.getUserSettings` | GET | `/api/client/settings/` | Клиентские настройки |
|
||||||
|
| `CoreApi.getUsers` | GET | `/api/core/users/` | Пользователи компании (`company`, `limit`, `offset`, `show_inactive`) |
|
||||||
|
| `CoreApi.getDepartments` | GET | `/api/core/admin/departments/` | Отделы компании |
|
||||||
|
| `CoreApi.getPositions` | GET | `/api/core/admin/positions/` | Должности компании |
|
||||||
|
|
||||||
|
### `gateway` — Gateway Sarex
|
||||||
|
|
||||||
|
Файлы `coreApi.ts`, `resources.ts`, `templatesApi.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `CoreApi.getGatewayUsers` | GET | `/api/v2/users/` | Пользователи по ресурсу/правам (`resource_id`, `permissions`, `limit`, `offset`) |
|
||||||
|
| `ResourcesApi.getResourceFullInfo` | GET | `/api/v2/resources/{resourceId}/` | Полная информация о ресурсе |
|
||||||
|
| `TemplatesApi.getTemplates` | GET | `/api/v1/disks/{diskId}/flat_documents/` | Плоский список документов диска (`type`) |
|
||||||
|
|
||||||
|
### `documentations` — Сервис документации
|
||||||
|
|
||||||
|
Файлы `documentation.ts`, `templatesApi.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DocumentationApi.getDocumentById` | GET | `/documents/{docId}` | Документ по id (опц. `extend`) |
|
||||||
|
| `TemplatesApi.getDisks` | GET | `/disks` | Список дисков |
|
||||||
|
|
||||||
|
### `eav` — Атрибуты и ассеты (EAV)
|
||||||
|
|
||||||
|
Файлы `assets.ts`, `attributes.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `AssetsApi.getAssetsPost` | POST | `/v4/assets/search/` | Поиск ассетов |
|
||||||
|
| `AssetsApi.getAssetsGet` | GET | `/v4/assets/` | Список ассетов |
|
||||||
|
| `AttributesApi.getAttributes` | GET | `/v0/attribute/` | Атрибуты компании (`company_id`) |
|
||||||
|
|
||||||
|
### `files` — Сервис файлов
|
||||||
|
|
||||||
|
Файл `documentation.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `DocumentationApi.getPage` | GET | `/pages/{sourceId}/{pageId}` | Страница файла (ответ `blob`) |
|
||||||
|
|
||||||
|
### `premises` — Сервис помещений
|
||||||
|
|
||||||
|
Файл `premises-api.ts`.
|
||||||
|
|
||||||
|
| Метод API | HTTP | Путь | Назначение |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `PremisesApi.getPremise` | GET | `/premises/{id}/` | Помещение по id |
|
||||||
|
| `PremisesApi.getPremisesFilter` | POST | `/premises/filter/` | Фильтрация помещений (`limit`, `offset` в query) |
|
||||||
|
| `PremisesApi.getPremiseTypesFilter` | POST | `/premise_types/filter/` | Фильтрация типов помещений (`limit`, `offset` в query) |
|
||||||
|
|
||||||
|
## Обработка ошибок
|
||||||
|
|
||||||
|
Глобальная обработка выполняется в `module/api/http-service.ts`: `httpService` обёрнут в `Proxy`, который для методов запросов (`getRequest`/`postRequest`/`putRequest`/`patchRequest`/`deleteRequest`) перехватывает ошибку и при статусе `403` показывает toast-уведомление (`react-toastify`) с текстом `detail` из ответа либо сообщением «У вас недостаточно прав для выполнения данного действия», после чего пробрасывает ошибку дальше. Для отдельных запросов показ уведомлений включается флагом `showErrorNotification: true`. В окружении `local` SDK переключается в режим `original` (`setSharedHttpServiceConfig({ type: "original" })`).
|
||||||
3278
apps/issues/openapi.yaml
Normal file
3278
apps/issues/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user