# Архитектура Монорепозиторий: `api/` (FastAPI) отдаёт REST, WebSocket и собранный фронт; `web/` — SPA на React. Основная бизнес-логика — PL/pgSQL-функции схемы `v3` в Postgres. Python-слой проверяет роли, валидирует запрос, вызывает функцию и маппит её ошибки. Поток редактирования ячейки: `RealtimeTable` → WebSocket `/api/v1/ws/form/{id}/sheet/{sheet}` (`cell_edit_start` — блокировка, `cell_updated`) → `SheetService` → `v3.upd_form_cell(s)`. Функция проверяет роль и окно этапа и пишет аудит `v3.log_event`, после чего изменение рассылается остальным подключённым. ## Модули | Модуль | Ответственность | Зависит от | |---|---|---| | `api/src/api/v1/` | Роутеры: auth, users, admin, audit, forms, projects, form_phases (`/stages`), phase_template, export, websocket, org_unit, vsp (`/dict/info`), expense_item, summary (`/svod`) | services, deps | | `api/src/api/v1/deps.py` | Текущий пользователь из JWT, гарды ролей | repository.user_repository, core.security | | `api/src/api/v1/websocket.py` | ConnectionManager, блокировки ячеек в памяти, события форм и FORM_4/проектов, синхронизация сателлитов AHR | services | | `api/src/services/` | Сервисы по сущностям, контроль доступа (`access_control_service.py`) | repository | | `api/src/services/export_service/` | Фоновые выгрузки в Excel: ProcessPool, таблица `export_job`, файлы в `tmp/exports`, маппинг колонок `export_mappings.py` | repository, openpyxl/XlsxWriter | | `api/src/repository/` | SQLAlchemy-запросы и вызовы SQL-функций `v3.*`, `SET LOCAL app.user_id` | db | | `api/src/db/` | engine, `Base`, сессия, ORM-модели (36) | core.config | | `api/src/domain/` | pydantic-схемы API, enums | — | | `api/src/core/` | Настройки, JWT/argon2, исключения и маппинг ошибок Postgres в HTTP | — | | `api/alembic/` | Миграции (около 49 ревизий); `versions/sql/*.sql` — DDL, функции, сиды, около 43 тыс. строк | Postgres | | `web/src/app/` | Роуты, AuthProvider (токены в localStorage) | pages | | `web/src/pages/` | Задачи (формы), карточка формы или проекта, проекты и их своды, свод 1 и 2, админка, справочники | components, api | | `web/src/components/RealtimeTable/` | Редактируемая таблица листа: WebSocket, блокировки, конфиги колонок `constants/FORM_*` | api | | `web/src/api/` | axios-клиент `/api/v1`, модули по сущностям, WebSocket выгрузок | — | ## Границы - Роутеры не ходят в БД напрямую, только через сервисы и репозитории. Сейчас нарушают `admin.py`, `main.py` (engine) и `websocket.py` (бизнес-логика в слое API). - Проверка прав на ячейки и окна этапов — в SQL. Python её не дублирует, только маппит ошибки. - Изменения схемы и функций — только новой миграцией alembic; старые миграции не правятся. - Фронт обращается только к `/api/v1` через `web/src/api/`. ## Открытые вопросы - Prod-авторизация через Keycloak (`raisa_fastapi_protected_api`) закомментирована в `main.py`, `deps.py`, `auth.py`; сейчас всегда работает локальный JWT. Как будет устроен prod? - `websocket.py:333`, `:357`: условие `form_key != form_key` всегда ложно, поэтому блокировки сравниваются по `line_id` между всеми формами; `release_locks_for_row` ничего не возвращает. - Раскладка колонок дублируется: `web/.../constants/FORM_1|2|4/*.js` (около 4 тыс. строк каждый) и `api/.../export_mappings.py`. Источник правды не определён. - Названия форм в БД и UI расходятся (FORM_2, FORM_4). - Мёртвый код фронта: `pages/TablePage.jsx`, `FormsPage.jsx`, `TablesTest.jsx`, `TableTempPage/`, `api/tables.js`, `api/client_form.js`, `api/ws.js`, `constants/FORM_1/*_NO_LOCAL.js`. - В `package.json` не объявлены `@mui/x-date-pickers`, `@mui/icons-material`; tailwind/daisyui не используются. - `Settings.CORS_ORIGINS` не применяется (в `main.py` `allow_origins=["*"]`), `APP_ENV` не читается, `JWT_SECRET` по умолчанию небезопасный. - `/` монтируется дважды в `api/src/main.py:289` и `:291`. - Интеграционные тесты зависят от данных в общей БД и не изолированы; `downgrade()` в миграциях пустые. - Python-линтеров и проверок CI в репозитории нет.