По ответам пользователя 03.10.2026: обмен через recognition_job перестроен на core NATS (queue group'ы, ack сервиса = конверт status=processing, повторная публикация ядром 30 с × 3 → dlq, дедуп по result_msg_id); capability ci-template заменена на quality-checks (make check / make check-full, конфигураций CI нет); compose: PostgreSQL 16, NATS без --jetstream, python:3.11-slim; пакеты raisa_* фильтруются при dev-установке, npm raisa-* → optionalDependencies. Обновлены 03-architecture, 09-delivery-plan, 00-workspace-layout, proposal/design/specs/tasks change'а; открытые вопросы к платформе закрыты, остались Node и JWT РАИСА. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
276 lines
28 KiB
Markdown
276 lines
28 KiB
Markdown
# 03. Архитектура (целевая, по решениям DECISION-1…5 и ARCH-* в DBML v5.13)
|
||
|
||
## Компоненты (= репозитории, см. `00-workspace-layout.md`)
|
||
|
||
```
|
||
┌──────────────┐ JWT (email) ┌──────────────────────────────┐
|
||
браузер ──────▶│ React SPA │ ───────────────▶│ arm-core (FastAPI) │
|
||
└──────────────┘ │ REST /api/v1, policy-слой, │
|
||
│ единственный писатель в БД │
|
||
└───────┬──────────┬───────────┘
|
||
│ │
|
||
PostgreSQL │ │ S3 (uploads/ results/ exports/ archive/)
|
||
│ │
|
||
NATS (core) ◀─────────────────┘ outbox │
|
||
recognition.tabular.request │
|
||
recognition.image.request │
|
||
recognition.result ───────────────────▶ arm-core (inbox, дедуп по msg_id)
|
||
│
|
||
┌───────────────┴───────────────┐
|
||
┌────────▼────────┐ ┌─────────▼────────┐ ┌──────────────────┐
|
||
│ tabular-parser │ │ scan-gateway │ ◀─────▶│ ИС Сканирование │ (внешняя, горячие папки)
|
||
│ XLS/XLSX/XLSB │ │ PDF/PNG/JPG │ └──────────────────┘
|
||
│ stateless, без БД│ │ stateless, без БД│
|
||
└─────────────────┘ └──────────────────┘
|
||
```
|
||
|
||
- **arm-core** — монолитное ядро: справочники, файлы/документы, маппинг результатов распознавания, консолидация
|
||
(вычисления), согласование, ЕХ, аудит, права. Один Alembic, одна схема.
|
||
- **tabular-parser** — читает файл из S3 `uploads/`, находит на листах ОСВ (§1) и формы ББ/ОФР (§12) **по содержимому**,
|
||
пишет нормализованный JSON в S3 `results/`, публикует `recognition.result`. Контракт результата —
|
||
`docs/contracts/recognition-result.schema.json` (черновик; финализировать в срезе 2). Правила распознавания
|
||
заголовков/группировок — `rules/*.yaml`.
|
||
- **scan-gateway** — адаптер ИС Сканирование: переименовать файл `UUID_имя.pdf`, положить в «Входящие», опросить
|
||
«Исходящие/UUID/UUID.json», сохранить сырой ответ в S3 `raw/{job_id}.json` (в БД не регистрируется), нормализовать
|
||
в тот же контракт результата, опубликовать `recognition.result`. Таймаут 5 мин → `timeout`.
|
||
- **Frontend** — React + TS по шаблону команды; экраны по макету `dkb_24.09.26` (справочник ГК, консолидации,
|
||
первичные документы, проверка распознавания, сводная ОСВ, шахматка, корректировки, гудвилл, КФО, ЕХ, журнал, роли).
|
||
|
||
## Контракт обмена через NATS (DECISION-4)
|
||
|
||
Схемы — `docs/contracts/` (v1.0): `recognition-request.schema.json` и `recognition-result-message.schema.json` — тела
|
||
сообщений, `recognition-result.schema.json` — файл результата в S3; примеры — `docs/contracts/examples/`.
|
||
|
||
| Поток | Subject | Кто пишет | Полезная нагрузка | `Nats-Msg-Id` |
|
||
|---|---|---|---|---|
|
||
| запрос | `recognition.tabular.request` / `recognition.image.request` | arm-core (outbox из `recognition_job`) | `schema_version`, `job_id`, `file_id`, `pipeline`, `file_type`, `original_name`, `s3 {bucket, key}` исходника, `reply_deadline`, `attempt` | `<job_id>:<attempt>` |
|
||
| результат | `recognition.result` | tabular-parser / scan-gateway | `schema_version`, `job_id`, `file_id`, `status` (done / needs_check / error), `error {code, message}`, `service {name, version}`, `result_s3 {bucket, key}`, `result_schema_version`, `raw_s3_key` (только scan-gateway), `metrics` | `result:<job_id>:<attempt>` |
|
||
|
||
`group_schema` в конверт не входит — ядро берёт его из `documents[].group_schema` файла результата при маппинге.
|
||
Конверт со `status = processing` — подтверждение взятия в работу (ack), `Nats-Msg-Id = ack:<job_id>:<attempt>`.
|
||
|
||
**На платформе NATS без JetStream** (ответ 03.10.2026): нет durable consumer'ов, серверного дедупа и redelivery.
|
||
Поэтому гарантии доставки — на уровне приложения: сервисы подписаны queue group'ами (`tabular-parser`, `scan-gateway`),
|
||
ядро — queue group `arm-core` на `recognition.result`; сервис сразу по получении запроса публикует ack `processing`;
|
||
ядро, не получив ack за `ACK_TIMEOUT_SECONDS` (30), публикует запрос повторно с тем же `Nats-Msg-Id`, не более
|
||
`OUTBOX_MAX_REPUBLISH` (3) раз, затем `dlq`. Сервис держит process-local множество `job_id` в работе и на повторный
|
||
запрос отвечает ack'ом без второй обработки. Появится JetStream — меняются только publisher/subscriber.
|
||
|
||
Правила:
|
||
- `recognition_job` создаётся в статусе `queued` **в одной транзакции** со `stored_file`; фоновый публикатор переводит в
|
||
`published` (`request_msg_id` = `Nats-Msg-Id`), ack сервиса — в `processing` (`started_at`). Повтор публикации — по
|
||
`queued` старше N секунд и по `published` без ack.
|
||
- Результат принимается подпиской queue group; `result_msg_id UNIQUE` = дедуп. Нет ack после повторов → `dlq`;
|
||
потерянный результат → `reply_deadline` → `timeout`; replay = сброс в `queued` с `attempt + 1`.
|
||
- Результат одного файла содержит **N документов**; ядро на каждый создаёт `document` и маппит данные.
|
||
- Сервисы не знают про БД и про бизнес-смысл: они возвращают структуру файла; нормализация счетов, резолв
|
||
контрагентов, проверки — в ядре.
|
||
|
||
## Схема ключей S3
|
||
|
||
**Один бакет** (`OPENBAO__MINIO__MINIO_BUCKET` шаблона); роль объекта — первый сегмент ключа:
|
||
`{bucket_role}/{file_id}/{job_id}/{name}`. Роли: `uploads` (исходники, срок — весь срок хранения), `results`
|
||
(нормализованные JSON; ретеншн — ARCH-010, открыт), `exports` (7 дней), `archive` (data lake). Все объекты регистрируются
|
||
в `s3_object` (`bucket_role`, `bucket`, `key`, sha256, size, expires_at). Сырой ответ OCR `raw/{job_id}.json` — по
|
||
конвенции, без записи в БД. Lifecycle-правила — по префиксам.
|
||
|
||
## Аутентификация и авторизация
|
||
|
||
1. SPA получает JWT от РАИСА; каждый запрос несёт `Authorization: Bearer`.
|
||
2. arm-core валидирует подпись/срок, извлекает email, находит `app_user` (`is_active`); нет пользователя — 403.
|
||
3. Права: `effective_permissions = ∪ role_permission(roles(user))`. Проверка в зависимости FastAPI
|
||
`require(permission_code)`.
|
||
4. **Policy-слой** поверх прав (отдельный модуль, чистые функции): видимость по филиалу (`regional_office`),
|
||
по ЗО (`business_segment`), допустимость действия по статусу версии (`consolidation_status`) и по активной
|
||
блокировке (`consolidation_lock`). Право говорит «может», policy — «эту ли и сейчас ли».
|
||
5. Локальная разработка: заглушка-эмитент JWT с теми же claims (feature-flag `AUTH_DEV_ISSUER`).
|
||
|
||
## Слои arm-core (по раскладке шаблона команды)
|
||
|
||
Структура — раскладка шаблона `api/src/` (см. «Соглашения шаблона» ниже); `tabular-parser` и `scan-gateway` — те же
|
||
каталоги без `web/`, `db/`, `models/`, `alembic/`. Соответствие прежним названиям этого документа: `backend/app/` →
|
||
`api/src/`, `integrations/` → `core/`, `migrations/` → `api/alembic/`, `frontend/` → `web/`.
|
||
|
||
```
|
||
api/src/
|
||
api/endpoints/ # роутеры FastAPI по контекстам: me, directories, files, documents, consolidation, kfo, ex, audit, users
|
||
api/dependencies/ # фабрики Depends: сессия, сервисы, current_user, require(permission)
|
||
domain/ # чистая предметная логика без I/O: правила ОСВ, периоды, матчинг, элиминация, проверки, пороги,
|
||
# машины статусов, policy — pydantic-модели на входе и выходе
|
||
services/ # use-cases: оркестрация domain + repositories + outbox + audit_event
|
||
repositories/ # SQLAlchemy-запросы, в т. ч. запросы-вычисления (сводная ОСВ, шахматка)
|
||
models/ # ORM 1:1 с DBML v5.13; имена таблиц/колонок/enum — как в DBML
|
||
schemas/ # pydantic DTO API /api/v1
|
||
core/ # config, clients (S3; NATS outbox-publisher и result-consumer), auth (JWT РАИСА / dev-эмитент),
|
||
# logger, exception_handlers
|
||
db/ # engine, сессия
|
||
api/alembic/ # миграции
|
||
api/scripts/ # check_dbml_vs_orm.py, seed
|
||
api/tests/ # фикстуры — из ../fixtures (ARM_KA_FIXTURES)
|
||
web/ # SPA
|
||
openspec/
|
||
```
|
||
|
||
Принципы: `domain/` тестируется без БД на фикстурах из `../fixtures`; вычисляемые объекты консолидации возвращаются
|
||
как pydantic-модели и **не** имеют ORM-таблиц; все мутации идут через `services/` с записью `audit_event`.
|
||
|
||
## Статусы и машины состояний
|
||
|
||
Три независимые машины: `file_status` (техника файла), `document_status` (проверка аналитиком), `consolidation_status`
|
||
(маршрут КФО) + `recognition_status` (жизненный цикл задания). Переходы — `05-domain-rules.md` §9. Реализовать как
|
||
явные таблицы переходов в `domain/`, а не `if`-ы по роутерам.
|
||
|
||
## Соглашения шаблона команды
|
||
|
||
Три сервисных репозитория развёрнуты из нулевого коммита репозитория команды `RSHB/DKB_ARMKA` (`ba172f8`, «Шаблон
|
||
FastAPI» платформы РАИСА/aurora): дерево импортировано как есть (`arm-core@ba92222`), доменный код удалён, каркас
|
||
оставлен (`8e1f423`). **Документы подстраиваются под шаблон, а не наоборот.** Ниже — что шаблон задаёт и что мы к нему
|
||
добавляем. Один и тот же текст действует для всех трёх репозиториев; отличия сервисов — в разделе «Что вырезано».
|
||
|
||
### Раскладка репозитория
|
||
|
||
```
|
||
<repo>/
|
||
README.md описание шаблона (в Срезе 0 заменяется описанием сервиса)
|
||
raisa_config.yml профиль ресурсов платформы (cpu/ram); читает платформа при деплое
|
||
setup/ бутстрап разработчика: setup.sh (venv + pip + npm i); pip.conf и .npmrc → внутренний Nexus банка
|
||
api/ backend. РАБОЧИЙ КАТАЛОГ для всех команд: от него ищутся .env, pytest.ini и web/ для раздачи SPA
|
||
run_server.py точка входа платформы: uvicorn, host/port из SERVER__HOST / SERVER__PORT, lifespan=on
|
||
requirements.txt прод-зависимости, pinned (fastapi 0.135, SQLAlchemy 2.0.36, alembic 1.14, asyncpg, pydantic 2.13,
|
||
pydantic-settings, nats-py 2.14, miniopy-async, smbprotocol, loguru, sentry-sdk, raisa_fastapi_protected_api)
|
||
requirements-dev.txt dev-зависимости (pytest-asyncio, pytest-env, openpyxl, …) — пины рассинхронизированы с requirements.txt
|
||
pytest.ini asyncio_mode=auto, loop scope session, env APP_ENV=test / ENV_FILE=.env.test, testpaths=tests
|
||
alembic.ini, alembic/ env.py берёт URL из настроек приложения, схема dkb, compare_type=True, include_schemas
|
||
src/
|
||
__init__.py экспортирует app и get_settings
|
||
app.py FastAPI: lifespan (SMB-сессия, MinIO, NATS), middleware (gzip, proxy headers, CORS, РАИСА при DEBUG=false),
|
||
GET /healthcheck, include_router, раздача SPA из api/web/ (SPAStaticFiles, fallback index.html)
|
||
api/router.py агрегирующий APIRouter; api/endpoints/* — роутеры; api/dependencies/* — фабрики Depends
|
||
core/config.py pydantic-settings (см. «Конфигурация»); модуль создаёт settings при импорте
|
||
core/clients.py клиенты инфраструктуры (S3Storage поверх miniopy-async)
|
||
core/nats_callback.py обработчики сообщений NATS
|
||
core/logger.py loguru: setup_logger, перехват std logging и uvicorn, request_id, шумовые пути
|
||
core/exception_handlers.py ошибки → JSON {"detail": …}
|
||
common/exceptions.py доменные исключения (NotFoundError, CouldntCreateError)
|
||
db/db.py async engine (asyncpg), search_path=dkb, get_async_session
|
||
models/base.py DeclarativeBase с MetaData(schema="dkb"), UUIDPrimaryMixin (uuid4 на стороне приложения)
|
||
schemas/ pydantic DTO и enum'ы API
|
||
repositories/ запросы: класс на таблицу, принимает AsyncSession
|
||
services/ use-cases
|
||
tests/conftest.py
|
||
web/ frontend (остаётся только в arm-core): React 18 + TS, rsbuild, antd, Redux Toolkit, biome
|
||
```
|
||
|
||
Наши дополнения к `api/src/`, не меняющие существующих каталогов: `domain/` — чистая предметная логика без I/O
|
||
(правила §1–§10, машины статусов, policy); `core/auth.py` — JWT РАИСА и dev-эмитент; `api/scripts/` — служебные скрипты
|
||
(`check_dbml_vs_orm.py`, seed). Пути в документах вида `rules/*.yaml`, `scripts/…`, `tests/…` отсчитываются от `api/`.
|
||
|
||
### Конфигурация
|
||
|
||
- `pydantic-settings`, файл `api/.env`, вложенность через `__`, лишние переменные игнорируются. Все инфраструктурные
|
||
секреты — под префиксом `OPENBAO` (в проде заполняет OpenBao): `OPENBAO__DATABASE__DATABASE_URL`,
|
||
`OPENBAO__MINIO__{MINIO_URL, MINIO_ADMIN_LOGIN, MINIO_ADMIN_PASSWORD, MINIO_BUCKET}`,
|
||
`OPENBAO__NATS__{NATS_URL, NATS_SEED, NATS_JWT}`, `OPENBAO__SMB__{SMB_HOST, SMB_PATH, SMB_LOGIN, SMB_PASSWORD}`.
|
||
Прочее: `DEBUG`, `ROOT_PATH`, `VERSION`, `SERVER__HOST`, `SERVER__PORT`.
|
||
- `settings` создаётся при импорте (`core/config.py`, `db/db.py`, `app.py`) — импорт почти любого модуля требует
|
||
полного окружения; тесты получают его из `.env.test`.
|
||
- `DEBUG=true` — режим разработки шаблона: NATS не подключается, middleware РАИСА не импортируется. Наши флаги
|
||
разработки (`AUTH_DEV_ISSUER`, `SCAN_EMULATOR`, режим горячих папок) добавляются так же — отдельными полями `Settings`,
|
||
по умолчанию выключены; в проде ни один не включается.
|
||
|
||
### База данных и миграции
|
||
|
||
- SQLAlchemy 2.0 async + asyncpg. Одна схема PostgreSQL **`dkb`** — зашита в шаблоне (MetaData, `search_path`,
|
||
`version_table_schema` и `include_name` Alembic). Следуем шаблону, не `public`.
|
||
- Alembic берёт URL из настроек, не из `alembic.ini`; ревизии — `api/alembic/versions/`, имя файла стандартное
|
||
`<rev>_<slug>.py`. Автогенерация — только черновик: итоговую ревизию сверяет с DBML `api/scripts/check_dbml_vs_orm.py`.
|
||
- PK везде `uuid` через `UUIDPrimaryMixin`; имена таблиц, колонок и enum'ов — ровно как в DBML v5.13.
|
||
|
||
### Инфраструктурные клиенты
|
||
|
||
- **S3** — `miniopy-async` (`Minio`, `cert_check=False`), **один бакет** `MINIO_BUCKET`; роли `s3_object.bucket_role`
|
||
становятся первым сегментом ключа (`uploads/…`, `results/…`, `raw/…`, `exports/…`, `archive/…`).
|
||
- **NATS** — `nats-py`; аутентификация nkeys-seed + JWT (`NATS_SEED`, `NATS_JWT`) через `user_jwt_cb`/`signature_cb`;
|
||
соединение и подписки создаются в lifespan и живут в `app.state.nats` / `app.state.nats_subs`. Core NATS без
|
||
JetStream: подписки с queue group, заголовки `Nats-Msg-Id` ставим сами, ack и повторы — на уровне приложения.
|
||
- **SMB** (горячие папки ИС Сканирование) — `smbprotocol`/`smbclient`, сессия регистрируется в lifespan; нужен только `scan-gateway`.
|
||
- **Внешняя аутентификация** — пакет `raisa_fastapi_protected_api` из внутреннего Nexus (`AuthorizationMiddleware`,
|
||
`ProtectedSettings`, …), импортируется только при `DEBUG=false`; фронтовая пара — `raisa-fastapi-protected-web`.
|
||
Вне сети банка оба пакета недоступны (см. вопросы Среза 0).
|
||
|
||
### DI, слои, ошибки, логи
|
||
|
||
- Контейнера DI нет: ручные фабрики `Depends` в `api/dependencies/` — фабрика получает `AsyncSession` из
|
||
`get_async_session`, собирает репозитории и сервис; инфраструктурные клиенты берёт из `request.app.state` (`storage`, `nats`).
|
||
- Поток запроса: `api/endpoints` → `services` → `repositories` → `models`; DTO — `schemas/`. Доменные исключения из
|
||
`common/exceptions.py` маппятся в HTTP в `core/exception_handlers.py` (RequestValidationError → 422, IntegrityError → 409,
|
||
SQLAlchemyError → 500, HTTPException как есть, прочее → 500); тело всегда `{"detail": …}`, тексты по-русски.
|
||
- Логи — loguru (`core/logger.setup_logger`): перехват std logging и uvicorn, `request_id` в extra, успешные обращения к
|
||
`/healthcheck`, `/docs`, статике не логируются.
|
||
|
||
### Тесты
|
||
|
||
- pytest + pytest-asyncio (`asyncio_mode=auto`, loop scope `session`) + pytest-env; запуск из `api/`; тесты в `api/tests/`.
|
||
- Тестовую БД шаблон не задаёт. Решение: тесты, которым нужна БД, идут на PostgreSQL из docker-compose (enum'ы,
|
||
partial unique, jsonb, `NULLS NOT DISTINCT` в SQLite не воспроизводятся); `domain/` тестируется без БД.
|
||
|
||
### Линтеры и типизация
|
||
|
||
- Backend: шаблон декларирует black / isort / pylint (бейджи README), конфигов не содержит. Принимаем `ruff`
|
||
(format — совместим с black; lint с правилами isort `I` и подмножеством pylint `PL`) и `mypy`; конфиг — `api/pyproject.toml`
|
||
(решение Среза 0; откат на black/isort/pylint — замена одного файла конфига и шага CI).
|
||
- Frontend: `biome` 1.9 (`npx biome check src`): ширина строки 140, одинарные кавычки, `bracketSameLine`,
|
||
`noExplicitAny` warn, `useAwait`/`noEmptyBlockStatements` error; типы — `npx tsc --noEmit` (strict). ESLint/Prettier нет.
|
||
|
||
### Сборка и деплой
|
||
|
||
- Платформа РАИСА запускает `api/run_server.py` с `SERVER__HOST`/`SERVER__PORT` из окружения; ресурсы — `raisa_config.yml`.
|
||
Python на платформе — **3.11** (ответ 03.10.2026); версия Node неизвестна (локально `node:22`).
|
||
- `Dockerfile`, `docker-compose.yml`, `.dockerignore` шаблон держит в `.gitignore` («Custom ignore»): образ собирает
|
||
платформа своим CI/CD, в который мы не лезем. Для локальной разработки: compose — на уровне workspace
|
||
(`docs/dev/docker-compose.yml`), `Dockerfile` (`python:3.11-slim`) — в корне каждого сервиса, строка `Dockerfile`
|
||
убирается из `.gitignore` (единственная правка шаблона; решение Среза 0).
|
||
- Фронт: `npm run build` (rsbuild) → `web/dist/` → копируется в `api/web/`, откуда FastAPI раздаёт SPA; роутеры
|
||
подключаются **до** mount'а `/`.
|
||
- Зеркала пакетов: pip и npm настроены на `nexus.dev.raisa.go.rshbank.ru` (`setup/pip.conf`, `setup/.npmrc`). Вне банка
|
||
Nexus недоступен до прода: dev-установка ставит `requirements.txt` без строк `raisa_*` (`make venv`), в
|
||
`web/package.json` пакеты `raisa-*` переносятся в `optionalDependencies`; импорты `raisa_fastapi_protected_api` условные
|
||
(`DEBUG=false`), как в шаблоне.
|
||
- CI в Gitea нет и не планируется; конфигураций CI в репозиториях нет. Проверки качества — одинаковые цели `Makefile`
|
||
во всех репозиториях: `make check` (ruff, mypy, pytest без Docker, идентичность контракта) и `make check-full`
|
||
(+ интеграционные тесты против compose, для arm-core — цикл миграций и `check_dbml_vs_orm.py`); `make check-full`
|
||
обязателен перед PR. Описание — `docs/dev/checks.md`.
|
||
|
||
### Frontend (только arm-core)
|
||
|
||
- React 18.3 + TypeScript 5.7 (strict), сборка rsbuild 1.6 (`npm run start | build | preview`); основной UI-кит — antd 6
|
||
(+ `@ant-design/icons`); установлены также tailwind 3 + daisyui 4 (тема `rshb`), `react-daisyui`, `raisa-ui` — точечно;
|
||
состояние — Redux Toolkit 2 (`store/store.ts`, слайсы в `store/slices/`, типизированные `useAppDispatch`/`useAppSelector`);
|
||
маршрутизация — react-router-dom 7 (`BrowserRouter basename=Config.APP_ADDRESS`, маршруты в `App.tsx`); `pdfjs-dist` — PDF.
|
||
- Структура `web/src/`: `layouts/` — страницы, `widgets/` — составные блоки, `components/` — примитивы, `store/`, `conf/`;
|
||
стили — CSS-модули (`styles.module.css`) и токены antd (`ConfigProvider` в `index.tsx`).
|
||
- Переменные окружения только с префиксом `REACT_APP_` (rsbuild `loadEnv`), читаются через `conf/config.ts`: все запросы к
|
||
бэку — `${Config.API_URL}/api/v1/…` (в сборке относительный путь); `REACT_APP_ROOT_PATH` = `ROOT_PATH` бэка
|
||
(по умолчанию `/aurora/apps/fastapi-dkb-armka`) задаёт `base`/`assetPrefix` rsbuild и `basename` роутера.
|
||
|
||
### Что вырезано в tabular-parser и scan-gateway
|
||
|
||
Сервисы распознавания — тот же шаблон без фронта и без БД (DECISION-4). На момент Среза 0 оба репозитория — полные копии
|
||
каркаса `arm-core`; change `slice-0-skeleton` каждого сервиса удаляет: `web/` целиком; `setup/.npmrc` и npm-шаги
|
||
`setup/setup.sh`; из `app.py` — mount SPA (`SPAStaticFiles`, `os.makedirs("web")`), CORS, импорт
|
||
`raisa_fastapi_protected_api` (внешнего API нет — только `/healthcheck`); `db/`, `models/`, `repositories/`, `alembic/`,
|
||
`alembic.ini` и `OPENBAO.DATABASE` из настроек; из `requirements.txt` — `asyncpg`, `SQLAlchemy`, `alembic`, `argon2-cffi`,
|
||
`raisa_fastapi_protected_api`; шаблонный `README.md` заменяется описанием сервиса. `tabular-parser` дополнительно без SMB
|
||
(`smbprotocol`, `OPENBAO.SMB`, регистрация сессии в lifespan). Остаются: `run_server.py`, `raisa_config.yml`, `setup/setup.sh`
|
||
+ `pip.conf`, `pytest.ini`, настройки `OPENBAO__MINIO__*` / `OPENBAO__NATS__*` / `DEBUG`, `core/config.py`, `core/clients.py`,
|
||
`core/nats_callback.py`, `core/logger.py`, `core/exception_handlers.py`, `common/exceptions.py`. Точный список — в
|
||
`CLAUDE.md` сервиса и в `tasks.md` его change'а.
|
||
|
||
## Что ожидаем от платформы (для запроса ресурсов)
|
||
|
||
По ответам платформы (03.10.2026): PostgreSQL **16** (одна БД, схема `dkb` — создавать схемы можно), NATS **без
|
||
JetStream** (3 subject `recognition.*`; аутентификация nkeys/JWT, как в шаблоне), S3-совместимое хранилище (один бакет;
|
||
префиксы-роли + lifecycle по префиксам), Python 3.11 в базовом образе, Nexus (pip/npm, пакеты `raisa_*`) — только внутри
|
||
банка, CI/CD — пайплайн платформы (мы отдаём команды `make check` / `make check-full`). Секреты — через переменные
|
||
окружения (`OPENBAO__*`). Открыто: версия Node, параметры JWT РАИСА.
|