MP Tool React — форк MP Tool на современном стеке (React 19 + TS + Refine 5 + AntD + FastAPI)
  • Python 73%
  • TypeScript 15.6%
  • Shell 11.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
mptool-react agent 62cda16ad8
All checks were successful
CI / frontend-build (push) Successful in 2m33s
CI / backend-tests (push) Successful in 7m12s
CI / frontend-tests (push) Successful in 3m10s
docs(changelog): S3 переключён на ho-u26-pg-sr2 — расхождение хранилищ устранено
Приложение писало во ВНУТРЕННИЙ SeaweedFS узла de-u26-pg-sr1 (seaweedfs:8333), а
presigned-ссылки строило на публичном домене s3.isdata.tech, который с 21.09 ведёт
на ho-u26-pg-sr2. Загрузка и выдача указывали в разные хранилища. Сейчас не было
сломано (активность близка к нулю), но любая новая загрузка развела бы хранилища.

MPTOOL_REACT_S3_ENDPOINT: seaweedfs:8333 → https://s3.isdata.tech

Проверено замером: потеряно при переключении 0 объектов (799 на de-u26-pg-sr1 против
800 на ho-u26-pg-sr2; разница — один файл, который есть только на ho-u26-pg-sr2),
расхождений по содержимому 0. Полный цикл на живом приложении: upload → stat →
presigned_url → скачивание HTTP 200 с верным содержимым.

Откат: вернуть seaweedfs:8333 в .env и перезапустить api/worker/beat/worker-retro.
SeaweedFS на de-u26-pg-sr1 не тронут — контейнер жив, данные на месте.
2026-09-27 18:56:23 +03:00
.forgejo/workflows fix(ci): «нет такой версии» было ложью — падал транзиентный таймаут pip 2026-09-24 10:17:46 +03:00
.husky fix(ci): CI был красным на каждом пуше — гейт не работал нигде 2026-09-24 09:18:15 +03:00
backend refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
ci fix(deploy): самописное из /etc/systemd в git + конфиг раннера под Forgejo 16 2026-09-21 20:54:47 +03:00
deploy fix(backup): перенос бэкапа БД на de-u26-pg-sr1 + фикс DSN для --network host 2026-09-21 22:15:20 +03:00
docs refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
frontend fix(ci): CI был красным на каждом пуше — гейт не работал нигде 2026-09-24 09:18:15 +03:00
graphify-out refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
infra refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
marketplaces-docs test(docs): тест актуальности документации API 3 площадок + gap accrual 2026-09-09 18:24:30 +03:00
memory docs(plan): актуализация по факту — 9.1б закрыт, реестр субагентов, оценка postgres-mcp 2026-09-17 22:36:44 +03:00
monitoring refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
reference docs(api): sync reference spectra — WB 287→294 (+7 FBS), Yandex-спека пересобрана из upstream redocly bundle 2026-09-21 04:08:57 +03:00
scripts refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
.env.example docs(hygiene): синхронизация доков после вывода .41 и переезда БД 2026-09-19 00:07:37 +03:00
.gitattributes feat: Graphify knowledge graph (кодовой базы + схема БД) как dev-инструмент 2026-08-27 20:13:32 +03:00
.gitignore revert(infra): откат неверного фикса .gitignore — он сломал прод-pull 2026-09-23 22:35:41 +03:00
.graphifyignore feat: Graphify knowledge graph (кодовой базы + схема БД) как dev-инструмент 2026-08-27 20:13:32 +03:00
AGENTS.md feat: Graphify knowledge graph (кодовой базы + схема БД) как dev-инструмент 2026-08-27 20:13:32 +03:00
CHANGELOG.md docs(changelog): S3 переключён на ho-u26-pg-sr2 — расхождение хранилищ устранено 2026-09-27 18:56:23 +03:00
docker-compose.prod.yml feat(deploy): extra_hosts для контейнеров к хост-БД (подготовка переезда) 2026-09-21 18:38:54 +03:00
docker-compose.tests.yml chore(retire): вывод .41 из эксплуатации — сняты все зависимости 2026-09-18 21:29:01 +03:00
PLAN.md refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00
README.md docs(glossary): единое информационное поле + каноническое имя движка mptool-infra 2026-09-24 09:39:38 +03:00
WIKI.md refactor(terms): второй проход — код, мониторинг, скрипты, история 2026-09-24 11:27:54 +03:00

MP Tool React

Самостоятельный проект анализа маркетплейсов на современном стеке: (Wildberries, Ozon, Yandex Market) + монетизация через Workspace (рабочие области), пакеты подписок, гранулярные фичи и RBAC.

Полностью автономен: собственная папка / БД / Docker-стек / сеть / Traefik / S3 (SeaweedFS) / git. Внешних зависимостей от других проектов нет.

Стек

  • Frontend: React 19 + TypeScript, Vite (rolldown), Refine 5, Ant Design 5, zustand, openapi-typescript, recharts; Prettier + Oxlint.
  • Backend: FastAPI (Python 3.12), SQLAlchemy 2 + Alembic, pydantic v2, Celery, cryptography (Fernet); Ruff + pytest.
  • БД: mptool_react на PostgreSQL 18 — de-u26-pg-sr1 (31.77.11.188, хост de-u26-pg-sr1), переезд 18.09.2026. Прежний узел .41 выведен из эксплуатации 18.09.2026 (реплика остановлена, метрики и off-host бэкап переведены на de-u26-pg-sr1).
  • Хранилище: SeaweedFS 4.47 (S3) для картинок товаров / аватаров — s3.isdata.tech, бакет mptool-react-files.
  • Деплой: собственный Docker-стек (10 сервисов: api / web / worker / worker-retro / beat / mq / cache / seaweedfs / traefik / forgejo), публикация через собственный Traefik (HTTPS Let's Encrypt). Домены: mptool-react.isdata.tech, git.isdata.tech, s3.isdata.tech.

Ключевые возможности

  • Workspace — изолированные рабочие области: люди + организации + подписки + платильщик.
  • Пакеты и фичи — конструктор тарифов (Plan), гранулярный динамический реестр фич (Feature).
  • Подписки — привязаны к Workspace, комбинируются (фичи всех активных объединяются).
  • RBAC — роли платформы (platform_*) и воркспейса (owner/admin/manager/member), передача владения.
  • Self-service регистрация — создаёт пользователя + Workspace(owner) + trial-подписку.
  • API-ключи WB/Ozon/Yandex — шифрование Fernet (секрет в _api_key_raw, маска в API). WB-ключ проверяется при добавлении и по кнопке (/probe): фазы probe→retro→steady, продавец/ИНН/статус токена видны в UI.
  • Ретро-синхронизация WB — из UI: создание задачи (orders_sales/finance/advert), прогресс в реальном времени, перезапуск упавших (/admin/retro/jobs).
  • Расписание синхронизаций — из UI: выключить шумную задачу или сменить интервал без правки кода и перезапуска beat (/admin/schedule; оверрайды в app_metastate).
  • Дашборды (раздел «Аналитика»): WB Dash, WB P&L (фильтрация по бренду/категории/предмету/SKU), WB Adv (рекламная аналитика: KPI, динамика, топы, платформы), OZON Dash; сравнение периодов, графики с градиентами, остатки WB, админ-версия рекламы по всем организациям; тёмная тема, экспорт XLSX.

Структура проекта

backend/
  app/
    main.py               # FastAPI entrypoint + подключение роутеров
    config.py             # pydantic-settings (env)
    database.py           # SQLAlchemy 2 engine/session
    models/               # модели (auth, billing, accounts, wb, oz, ya, finance)
                          #   schema_invariants.py — общие имена UNIQUE/индексов для моделей И миграций
    api/                  # роутеры (auth, register, workspace, billing, keys,
                          #   dashboard, files, admin_users, routers=GenericCRUD)
    services/             # permissions (RBAC), feature_catalog, meta_state (версии алгоритмов),
                          #   day_range (sargable-фильтр по суткам для timestamptz-колонок)
    tasks/                # celery-задачи синхронизации, crypto (Fernet)
                          #   wb_dim_tasks.py — единый справочник размерностей:
                          #   sync_subject_map / build_product_dim / normalize_dimensions
    alembic/versions/     # миграции (billing, workspace, keys-encrypt, ...)
  tests/                  # pytest (164 теста, 18.09.2026)
frontend/
  src/
    App.tsx               # Refine + роутинг + меню
    pages/                # Dashboard, Workspace, Packages, Features, Subscriptions, Keys,
                          #   Retro (ретро-синхронизация), Schedule (расписания beat), ...
    api/                  # dataProvider, authProvider, s3
    store/                # zustand (workspaceId, theme)
docker-compose.prod.yml   # полный продуктивный стек
monitoring/               # отдельный стек мониторинга (Prometheus+Grafana, см. monitoring/README.md)
scripts/deploy.sh         # деплой с пиннингом образов по SHA и автооткатом
WIKI.md                   # полная документация (архитектура, роли, API, эндпоинты)
PLAN.md                   # план разработки (Workspace-модель и фазы)

Локальная разработка

Требования: Node.js 24 + npm 11, Python 3.12, uv (Python-менеджер зависимостей).

# backend
python3 -m uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install -r requirements.txt -r requirements-dev.txt
uvicorn app.main:app --reload

# frontend
cd frontend && npm install && npm run dev

Тесты и качество (Dev-инструменты — ОБЯЗАТЕЛЬНЫ при кодинге)

Актуальные версии инструментов: Ruff 0.16.2, pytest 8.3.4, pytest-asyncio 0.24.0, mypy 1.16.0, vitest 4.1.11, Oxlint 1.79, TypeScript 7.0.2, Prettier 3.9.6.

# backend (ВАРИАНТ Б: тесты на реальном PostgreSQL — тестовая БД mptool_react_tests (de-u26-pg-sr1))
# Задайте MPTOOL_REACT_DATABASE_URL в своём окружении (креды тестовой БД не храним в README).
# Пример формата: postgresql+psycopg://<user>:<pass>@<host>:5432/mptool_react_tests
cd backend && .venv/bin/ruff check app tests
export MPTOOL_REACT_DATABASE_URL="postgresql+psycopg://mptool_react_tests:<TEST_PASSWORD>@<TEST_DB_HOST>:5432/mptool_react_tests"
# В CI тот же DSN лежит в repo-secret MPTOOL_REACT_TESTS_DB_URL (см. ci/README.md) — sqlite не используется.
.venv/bin/python -m pytest tests/        # все тесты на PG (схема через alembic; контейнер сбрасывает кэш фич)
.venv/bin/python -m pytest tests/ --cov=app --cov-report=html                   # покрытие
.venv/bin/mypy --explicit-package-bases --config-file=pyproject.toml app  # строгая типизация (+ infra: 15 файлов)
./alembic_check.sh                 # проверка миграций: единственный head, цепочка без разрывов, сверка модели↔БД
                                   # python берётся из .venv (или PYTHON=/путь/к/python); --no-db — без сверки с БД
# ⚠️ 18.09.2026: тестовая БД переехала на de-u26-pg-sr1 (31.77.11.188); хост в conftest больше не фиксирован
# frontend
cd frontend && npm run build          # tsc -b + сборка
npm run typecheck                      # tsc --noEmit (быстрая проверка типов)
npm run lint                           # oxlint src/
npm run test                           # vitest (52)
# диагностика прода ОДНИМ вызовом (см. AGENTS.md «⚡ Скорость»):
#   export SSHPASS; ./scripts/prod_probe.sh [all|stack|data]npm run test:cov                       # vitest + coverage (v8)
# адаптивность (ОБЯЗАТЕЛЬНО при правках UI): аудит всех страниц на 3 вьюпортах (390/768/1440)
cd ~/.openclaw/workspace-mptool-react/audit-mobile && node audit.js
#   требует playwright + токен доступа в /tmp/token.txt (chmod 600); файл ВРЕМЕННЫЙ —
#   удалять сразу после прогона (живой токен в /tmp = утечка, см. AGENTS.md)
# git-хук pre-commit: prettier(lint-staged) + oxlint + tsc --noEmit + ruff (авто, обязательно чисто)

🩺 Разбор дефекта и верификация (правила, 17.09.2026)

Применяется при любом разборе дефекта и решении «внедрять ли». Введено по решению хозяина (практики взяты из GitHub Spec Kit, адаптированы — читались первоисточники, не README). Полный текст правил — в AGENTS.md, раздел «Разбор дефекта и верификация» (здесь только суть, чтобы не дублировать источник).

Триада, шаги не смешивать: диагноз (корневая причина + воспроизведение) → фикс (по корню, ponytail) → верификация (симптом ушёл И регрессий нет). Нельзя чинить, не записав диагноз.

Вердикт верификации — три градации: verified · partial (симптом ушёл, но есть регрессия/часть проверок неинформативна) · failed.

⚠️ Запрет ложной верификации: зелёные тесты не дают verified, если в диагнозе записано воспроизведение, которое фактически не выполнялось. Честный вердикт — partial. Дефект, наблюдавшийся в кабинете МП или на проде, проверяется тем же путём (psql/curl/ скриншот), а не только pytest.

Таблица проверок (обязательна, две группы — симптом и регрессия):

Check Command Result Notes
Воспроизведение (после фикса) из диагноза pass / fail / skipped / not-run причина
Новые/обновлённые тесты pytest tests/… pass / fail
Регрессия (backend) ruff · pytest · mypy pass / fail
Регрессия (frontend) tsc · oxlint · vitest pass / fail
Миграции alembic_check.sh pass / skipped
UI аудит адаптивности pass / skipped

⚠️ Инструмент недоступен → not-run с причиной, не пропускать молча (наш случай: pytest в git-хуке пропускается без тестового DSN — это должно называться явно).

Decision-gate «внедрять ли» (scorecard, 6 критериев: ценность проблемы · сила доказательств · ценность против бездействия · реализуемость · стратегическое соответствие · риск): go требует силы доказательств adequate+; при weak/unknown вердикт — needs-clarification. kill — это успех, а не провал, с прямой причиной.

Прочее из тех же правил: path safety (симлинки/выход за корень — отказать, не следовать; корни вычислять от __file__/git rev-parse, не хардкодить); URL Trust Policy (RFC1918 192.168.* и loopback — отказ; фиксировать сработавшую ветку); чек-лист заполняет проверяющий, не исполнитель.

🔀 Консистентность при нескольких агентах (scripts/)

Агенты и субагенты работают параллельно в одном репозитории. Конфликты убираются по категории ресурса — сначала изоляция/вычисление, блокировки только где ресурс физически один:

Категория Инструмент Зачем
Изолируемое git worktree add ../wt-<задача> -b <ветка> своё дерево, общий .git — конфликта нет
Вычисляемое scripts/changelog_tool.py --check/--fix номер записи вычисляется из файла, не назначается
Общий ресурс scripts/agent_lock.sh <имя> -- <cmd> ровно один писатель (тестовая БД, прод)
Общая ветка scripts/git_sync.sh -m "..." flock → fetch → rebase → push с повторами
python3 scripts/changelog_tool.py --check   # 0 = канон, 1 = коллизия (зовётся из pre-commit)
scripts/git_sync.sh -m "сообщение"          # коммит + сериализованный push
scripts/agent_lock.sh тестовая-БД -- python -m pytest tests/   # 75 = ресурс занят

⚠️ pre-commit проверяет нумерацию CHANGELOG, только когда он в staged — чтобы не тормозить коммиты. Локи живут в $GIT_COMMON_DIR/agent-locks/ (вне git, общие для всех worktree).

📋 Сбор работы субагентов (scripts/subagent_worklog.py)

Результаты субагентов рассеяны по сессиям — из PLAN.md их не видно. Инструмент собирает структурированный источник (openclaw tasks --json --runtime subagent) и сверяет его с планом:

python3 scripts/subagent_worklog.py            # что сделали + есть ли след в CHANGELOG/memory/PLAN
python3 scripts/subagent_worklog.py --check    # 1 = успешный прогон без следа (для автоматизаций)
python3 scripts/subagent_worklog.py --since 2026-09-01 --json

⚠️ Плейбуки лежат в воркспейсе агента (~/.openclaw/workspace-mptool-react/memory/), а не в репозитории — инструмент проверяет оба каталога.

🩺 Эксплуатация и надёжность (scripts/, deploy/)

Полный порядок действий — в docs/RUNBOOK.md. Инструменты:

Инструмент Зачем
scripts/verify_stack.sh health-gate: сервисы, HTTP, readiness БД, потребители очередей, краш-лупы
scripts/stack_watchdog.sh то же + алерт в локальный webhook (JSONL на проде + Telegram); гоняется таймером каждые 5 мин
scripts/config_validate.sh статическая проверка ПЕРЕД применением: compose + promtool + bash -n
scripts/backup_db.sh ночной бэкап (systemd-timer mptool-backup-db, 04:15 MSK) + off-host копия на de-u26-pg-sr1; пароль через env, не argv
scripts/restore_drill.sh учение восстановления: дамп → отдельная БД → сверка счётчиков → verdict (запуск на .41 под sudo)
scripts/deploy.sh деплой с тегом по SHA, авто-миграциями, verify, --rollback и push в реестр
scripts/pitr_base_backup.sh / pitr_drill.sh PITR: базовая копия кластера + учение восстановления на точку времени (узел de-u26-pg-sr1)
scripts/wal_cleanup.sh чистка WAL-архива PITR + ретенция копий (таймер каждые 15 мин, узел de-u26-pg-sr1) — инцидент 16.09.2026
scripts/setup_replication_primary.sh, setup_replica.sh, replica_check.sh, promote_replica.sh выведены из работы 18.09.2026 вместе с репликой (узел .41 погашен): содержимое осталось только как справка для аварийного сценария
./scripts/config_validate.sh     # ПЕРЕД up/деплоем (дешевле инцидента)
./scripts/verify_stack.sh        # 0 = стек работает
sudo /opt/mptool-react/scripts/restore_drill.sh   # на .41: проверить, что бэкап восстанавливается

Таймеры (systemd): mptool-react-stack.service (старт + гейт), -watchdog.timer (5 мин), -prune.timer (вс 05:30, чистка образов/кэша), mptool-react-monitoring-verify.timer.

Временные зоны (проверено фактом 15.09.2026):

Слой Зона Как задаётся
Приложение (api/web/worker/beat/mq/cache/traefik) MSK TZ (якорь x-tz)
Мониторинг (prometheus, alertmanager, grafana, экспортёры) MSK TZ + bind-mount zoneinfo
Хосты .40 и .41 MSK timedatectl (переведены 15.09.2026)
Cron de-u26-pg-sr1 MSK время задаём прямо в MSK (15 4 * * * = 04:15 MSK)
systemd-таймеры MSK зона суффиксом в OnCalendar (Sun *-*-* 05:30:00 Europe/Moscow)
Celery beat MSK timezone=Europe/Moscow + enable_utc=True
PostgreSQL: timezone (зона ДАННЫХ) UTC (явно) timezone = 'Etc/UTC' — от неё зависят date::date и EXTRACT(HOUR)
PostgreSQL: log_timezone (метки логов) MSK log_timezone = 'Europe/Moscow'

⚠️ Разделение зоны данных и зоны логов в PostgreSQL — намеренное. timezone влияет на date::date и EXTRACT(HOUR ...) в отчётах и прекомпьютах, поэтому зафиксирована явно в UTC и не меняется со сменой зоны хоста. log_timezone влияет только на метки в журнале. На бывшей реплике то же самое — иначе после promote группировка по дням сместилась бы на 3 часа.

⚠️ Ловушки, проверенные на практике:

  1. ключа Timezone= в секции [Timer] системы systemd не существует — он молча игнорируется (Unknown key name 'Timezone' ... ignoring), таймер идёт по TZ хоста;
  2. смена зоны хоста переинтерпретирует cron в новой зоне: 15 1 (был 04:15 MSK) стал бы 01:15 MSK. Пересчёт выражения обязателен — scripts/set_host_tz.sh делает это сам (ядро — scripts/lib/tz_shift_cron.py), сохраняя фактическое время запуска;
  3. брать время запуска cron надо из факта (cron.log, имена файлов дампов), а не из выражения;
  4. проверить зону контейнера через date нельзя, если date — BusyBox (prometheus/экспортёры): он берёт зону из localtime, а не из TZ, и врёт UTC. Смотреть логи (time=...+03:00);
  5. в образах forgejo, seaweedfs, cadvisor нет tzdata — TZ не работает без bind-mount /usr/share/zoneinfo.

Персистентность: именованные тома mq_data / cache_data (AOF) / celerybeat_data — очереди и rate-limit-ключи переживают пересоздание контейнеров.

🔎 Проверка утечек секретов (scripts/check_secret_leaks.sh)

Ищет пароли/токены проекта в логах, дампах, рабочем дереве, git HEAD, argv процессов и системных логах. Создан по следам инцидента 14.09.2026 (пароль БД в cron.log; след в /var/log/auth.log от передачи пароля аргументом).

./scripts/check_secret_leaks.sh                              # без root (системные логи → WARN)
echo "$LOCAL_SUDO_PASS" | sudo -S ./scripts/check_secret_leaks.sh   # полная
# коды: 0 = чисто, 1 = утечка, 2 = проверка неполная

⚠️ Скрипт сам не передаёт секреты в argv (иначе проверка создаст утечку).

Стиль кодинга — ponytail (ВКЛЮЧЁН, решение хозяина 17.09.2026)

Скилл ponytail v4.9.0 (ClawHub, @dietrichgebert/ponytail) — режим «ленивого сеньора»: YAGNI, сначала stdlib и нативные фичи платформы, без абстракций «на будущее». Парный скилл ponytail-audit — разовый аудит репозитория на переусложнение.

Статус: включён; уровень — lite (постоянно с 14.09.2026; 16.09 уровень full → ultra; 17.09 кратко отключён и в тот же день возвращён; уровень понижен до lite 17.09.2026).

lite = сделать что просят, но в одну строку назвать более ленивую альтернативу — выбор за хозяином. Уровень меняется командой /ponytail lite|full|ultra и закреплён в якорях AGENTS.md/MEMORY.md (сам по себе он session-scoped и в новой сессии сбросился бы к дефолту full).

  • Включён в двух местах, иначе режим не закрепится (он живёт в контексте, а не в скилле):
    1. ~/.openclaw/openclaw.json → skills.entries.ponytail.enabled = true — штатный механизм (бэкап конфига: openclaw.json.bak-ponytail-on-20260917-173411);
    2. секция «Стиль кодинга (ponytail)» в AGENTS.md — предписание YAGNI-лестницы.
  • Заголовок секции в AGENTS.md не переименовывать: он зафиксирован в agents.defaults.compaction.postCompactionSections, поэтому после сжатия контекста возвращается актуальный статус, а не пустое место.
  • Установка: skills/ponytail/, skills/ponytail-audit/ (воркспейс агента).
  • Выключение (по команде хозяина): openclaw config set skills.entries.ponytail.enabled false --strict-json + снять предписание в AGENTS.md. Уровень — /ponytail lite|full|ultra.
  • ⚠️ Обязательные процессы проекта остаются в силе — они никогда не были частью ponytail: README/PLAN/WIKI, ruff+pytest+mypy, tsc+oxlint+vitest+build, alembic_check.sh, аудит адаптивности, плейбук+CHANGELOG при изменении математики P&L.
  • ⚠️ Вывод ponytail-audit — гипотезы, а не решения: сверять с фактами (аудит 14.09 переоценил экономию более чем вдвое: −322 против фактических −98).

Graphify — граф знаний кодовой базы (локально, без LLM)

Карта связей проекта (функции/классы/модули frontend↔backend + схема БД PostgreSQL) в виде knowledge graph. Позволяет агенту искать по коду запросом вместо грепания файлов.

# установлен через uv tool (graphifyy); граф лежит в git (graphify-out/)
graphify query "какие функции считают P&L"     # scoped-подграф по вопросу
graphify path  "<A>" "<B>"                     # путь между двумя сущностями
graphify explain "<концепт>"                   # узел + его связи
graphify update .                              # обновить граф после правок кода (AST, 0 LLM)
graphify cluster-only .                        # пересобрать отчёт/HTML после ручных правок графа

# Мост «модель → таблица БД» (MAPS_TO): восстанавливается авто-хуком post-commit.
# Запускать вручную после полного extract:
python3 scripts/graphify_add_model_edges.py

⚠️ Карта БД обновляется АВТОМАТИЧЕСКИ хуком post-commit (раз в 7 дней, скрипт scripts/graphify_refresh_db_map.py); принудительно — python3 scripts/graphify_refresh_db_map.py --force. Раньше снимок устаревал молча (в сентябре 2026 не хватало 5 таблиц → терялись MAPS_TO-рёбра). Если обновление нужно вручную (интерпретатор — из uv tool, не системный python3, иначе ModuleNotFoundError: graphify):

GPY=/home/alex/.local/share/uv/tools/graphifyy/bin/python
PGDSN="<DSN>" $GPY -c "import os,json;from graphify.pg_introspect import introspect_postgres as i;json.dump(i(os.environ['PGDSN']),open('/tmp/pg_fresh.json','w'),ensure_ascii=False)"

Порядок после любой переинтроспекции: python3 scripts/graphify_add_model_edges.py (мост MAPS_TO) → graphify cluster-only . (отчёт/HTML).

Актуальность (на 17.09.2026): граф 2105 узлов / 3862 ребра, карта БД 70 pg-узлов, MAPS_TO 68 рёбер, 215 сообществ с семантическими именами. Проверено: graphify update . карту БД и мост НЕ теряет (это главный инвариант эксплуатации).

label-бэкенд (имена сообществ, LLM) — рабочий рецепт на 17.09.2026: PlusVibe через openai-совместимый эндпоинт, ключ подставляет secret egress proxy (в окружении — sentinel, plaintext не используется):

export OPENAI_BASE_URL="https://plusvibeapi.ru/v1"
export OPENAI_API_KEY="$PLUSVIBE_API_KEY"   # sentinel → подстановка на прокси
graphify label . --backend openai --model deepseek-v4.1-flash --max-concurrency 2

⚠️ max_tokens задавать с запасом: модель тратит токены на reasoning, при малом лимите — 502 reasoning_budget_exhausted. Исторический рецепт (RouterAI) сохранён в AGENTS.md, правило №6.

hooks: post-commit/post-checkout авто-перестраивают граф; merge driver union для graph.json.

Context7 (MCP-доступ к документации)

Установлен @upstash/context7-mcp@4.0.4 через OpenClaw MCP. Позволяет запрашивать актуальную документацию сторонних библиотек (Refine, FastAPI, AntD, SQLAlchemy и др.) без поиска вручную.

# конфигурация в ~/.openclaw/openclaw.json → mcp.servers.context7
# использование: инструмент context7__query-docs после context7__resolve-library-id

Отладка

  • Backend (FastAPI): python3 -m pip install --user --break-system-packages debugpy; запуск backend/run_debug.sh (debugpy слушает порт 5678) → VS Code «Remote Attach» (готовый конфиг в .vscode/launch.json).
  • Frontend (Vite): запуск через VS Code-конфиг «Vite (Node inspect)».

Оркестрация синков (очереди Celery) и P&L по дням

  • Раздельные очереди: wb-fast (ежедневные синки + пересчёт P&L) и wb-retro (длинные глубокие ретро) — без конкуренции/накопления.
  • Воркеры: mptool-react-worker (wb-fast, concurrency=2), mptool-react-worker-retro (wb-retro, concurrency=1).
  • Redis dedup-локи против накопления: advert/media/deep-retro/build_finance_daily.
  • Прекомпьют P&L — оконный пересчёт (17.09.2026): build_finance_daily пересобирает не всю историю, а только (org, день), чьи строки детализации менялись с прошлого успешного прогона (водяной знак wb.finance_daily.watermark по synced_at, TEMP _fd_scope). День перестраивается целиком (предикат на DELETE+INSERT+UPDATE) — иначе размазывание орг-расходов считалось бы от устаревшей базы. full=True — принудительно вся история (импорт себестоимости, дедуп biz_hash, ручной recompute_pnl); beat: окно */20 мин + полная 04:20 как страховочная сетка. Прежний режим (DELETE+залив всей истории каждые 4 ч) давал 2.52 перезаливки/час и раздул heap до 479 МБ против 54 МБ живых данных — см. memory/2026-09-17-precompute-window.md.
  • P&L по дням: финансы (wb_financedaily.rr_date), реклама (wb_advertcampaignstats.report_date), медиа (wb_mediadaily.report_date).
  • Продвижение (P&L) в wb_financedaily раскрывается на строки (UI) как у indeepa: «Оказание услуг ВБ.Продвижение» (= promotion_advert, источник wb_advertcampaignstats), «ВБ.Медиа» (= promotion_media), Аванс/Возврат «Баллы за отзывы» + Списание + Витрина (из promo_services_breakdown) + Подписка «Джем». Источник advert — кампании (НЕ deduction); разбивка строк не меняет сумму Продвижения.
  • Лимиты WB per-аккаунт (по ключу), не IP → параллельные эндпоинты безопасны (429 при превышении).

Деплой на прод (u24-sr1-soft)

Штатный способ — scripts/deploy.sh (сборка → миграции → запуск → verify, с автооткатом):

cd /opt/mptool-react && git pull
./scripts/deploy.sh              # полный деплой текущего HEAD
./scripts/deploy.sh --no-build   # только миграции + перезапуск
./scripts/deploy.sh --status     # текущий/предыдущий тег образов
./scripts/deploy.sh --rollback   # откат на предыдущий тег

Механика отката: образы тегируются по короткому SHA (не только :latest); состояние — в .deploy-state / .deploy-state.prev (вне git). Тег передаётся compose через MPTOOL_REACT_TAG (дефолт latest — обратная совместимость). Скрипт требует чистого git-дерева, применяет миграции до переключения и при провале verify_stack.sh автоматически откатывается на предыдущий тег.

Ручной эквивалент (если скрипт недоступен):

git pull && docker compose -f docker-compose.prod.yml up -d --build
docker exec -w /app mptool-react-api-1 alembic upgrade head

После деплоя FastAPI-документация доступна по внешним адресам:

  • Swagger UI: https://mptool-react.isdata.tech/api/v1/docs
  • OpenAPI JSON: https://mptool-react.isdata.tech/api/v1/openapi.json

Генерация типов фронтенда из продового OpenAPI:

cd frontend && npm run typegen        # из продового API
npm run typegen:local                 # из локального http://localhost:8000/api/v1/openapi.json

Бэкапы БД (ночной systemd-timer)

Скрипт scripts/backup_db.sh (таймер mptool-backup-db.timer, 04:15 MSK; юнит — deploy/): pg_dump в контейнере postgres:18.4 → .tmp → валидация gzip → публикация; хранит KEEP_DAYS (14) дней, исключает схемы staging/backup_2608.

⚠️ Пароль/credentials НЕ пишутся в лог. Всё, что идёт в cron.log, проходит через redact() (sed -E 's#://[^[:space:]]*@#://***@#g'): log(), хвост stderr pg_dump и stderr gzip -t. Добавляя новую запись в лог — только через log() или с явным redact, иначе DSN с паролем утечёт (дефект 14.09.2026: cron.log содержал 30 строк с паролем роли mptool_react).

⚠️ Права дампа — 600 (umask 077 в скрипте). Дамп содержит все данные БД; до аудита 15.09.2026 файлы выходили 664 — world-readable для любого пользователя узла.

Свежесть бэкапа контролируется мониторингом (backup_last_success_timestamp_seconds, алерт при возрасте >30 ч) — чтобы молчаливый отказ не повторял инцидент 10.09.

Мониторинг и алерты

Отдельный стек monitoring/ (Prometheus + Grafana + Alertmanager + экспортёры) — не перезапускается деплоем приложения. Grafana: https://monitor.isdata.tech. 6 дашбордов (Обзор, Контейнеры, Хосты, PostgreSQL, Внешние проверки, Мониторинг-самоконтроль) — автозагрузка через provisioning; генерируются monitoring/grafana/generate_dashboards.py. Метрики хоста/контейнеров/БД + срок TLS-сертификатов + качество данных (дубли P&L, свежесть бэкапа, stale biz_hash) + OOM-события + самоконтроль контура (Prometheus/Alertmanager up, доставка алертов, TSDB). Алерты фиксируются на de-u26-pg-sr1, опционально — Telegram. ⚠️ Конфиги монтируются каталогами, не файлами: single-file bind mount держит inode и после git pull контейнер видит старое содержимое (инцидент 19.09.2026). Подробности и состав — monitoring/README.md.

⚠️ Метрики качества данных пишет scripts/data_quality_metrics.sh по systemd-таймеру mptool-data-quality (*:0/15). Лог — /opt/backups/mptool-react/var/metrics.log (НЕ /var/log: каталог принадлежит root:syslog 775, alex в группе syslog не состоит → cron не мог открыть редирект и скрипт не запускался вообще — дефект найден аудитом 15.09.2026). Ротация — deploy/logrotate-mptool-metrics. Свежесть .prom стережёт алерт CriticalPromFileStale.

Документация

Полное описание архитектуры, ролей, админ-панели, URL-карты API и монетизации — в WIKI.md. План разработки и статус — в PLAN.md.

Правила поддержки документации

При каждом изменении проекта синхронно обновлять: PLAN.md (статус/этапы), README.md (структура/стек/команды), WIKI.md (архитектура/роли/эндпоинты). Три файла — единый источник правды для проекта.