Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m3s
Conventional commits / commits (pull_request) Successful in 24s
Backend security / security (push) Successful in 7s
Backend verify / verify (push) Successful in 2m10s
165 lines
11 KiB
Markdown
165 lines
11 KiB
Markdown
# WealthLedger backend
|
||
|
||
NestJS API WealthLedger. Backend владеет auth, SQLite-данными и server-side
|
||
интеграцией T-Bank; frontend не обращается к T-Bank напрямую.
|
||
|
||
## Быстрый старт
|
||
|
||
Требуется Node 24 (`.nvmrc`). Создайте `.env` из примера, замените placeholder
|
||
секреты и установите зависимости строго из lockfile:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
nvm use --silent
|
||
npm ci
|
||
npx prisma migrate deploy
|
||
npm run start:dev
|
||
```
|
||
|
||
`JWT_SECRET` должен быть секретом не короче 32 символов, а
|
||
`TOKEN_ENCRYPTION_KEY` — base64-кодированным 32-байтным ключом. Не добавляйте
|
||
T-Bank token в `.env`: он вводится пользователем и хранится в зашифрованном
|
||
виде.
|
||
|
||
## Проверка
|
||
|
||
Единый completion gate не изменяет versioned-файлы:
|
||
|
||
```bash
|
||
npm run verify
|
||
```
|
||
|
||
Он запускает zero-warning lint,
|
||
application/test typechecks, Jest и build. `npm run lint:fix` — отдельная
|
||
записывающая команда, `npm run test:cov` — диагностика без общего числового
|
||
threshold. `npm run test:e2e` отдельно проверяет все HTTP suites на новых базах
|
||
и runtime OpenAPI с соседним frontend checkout. Для полной backend-проверки
|
||
выполните обе команды.
|
||
|
||
Nest 12 поставляется как ESM. Скрипты Jest включают `--experimental-vm-modules`,
|
||
включая watch/coverage/debug; SQLite runner включает тот же флаг. TypeScript 6
|
||
использует явные `rootDir` и типы Node/Jest в `tsconfig.json`. Переход на
|
||
TypeScript 7 требует поддержки со стороны `ts-jest` и `typescript-eslint`.
|
||
|
||
### CI и обновления зависимостей
|
||
|
||
`.gitea/workflows/verify.yml` запускает `npm ci`, генерацию Prisma client,
|
||
`npm run verify` и `npm run test:e2e` на push и PR. Frontend snapshot для
|
||
разработки берётся из `develop`; для `main`, PR в `main` и `release/*` — из
|
||
полного SHA в `.ci/refs.json`. Лог сохраняет оба SHA. Изменение
|
||
контракта согласуется с этой frontend revision. Успешный unit gate не заменяет
|
||
SQLite/e2e; ошибка любой suite завершает job с FAIL.
|
||
|
||
`security.yml` сканирует всю Git-историю Gitleaks 8.30.1 с полностью скрытыми
|
||
значениями и выполняет `npm audit --include=dev --audit-level=low` при push,
|
||
PR и ежедневно. `.gitleaksignore` перечисляет только проверенные исторические
|
||
совпадения: детерминированные fixtures, placeholder и badge из Nest starter.
|
||
Новые коммиты этими исключениями не покрываются.
|
||
|
||
Renovate группирует minor/patch updates, а major требует согласования в
|
||
Dependency Dashboard. Автоматический merge не включён. Прямые зависимости
|
||
фиксируются точными версиями: `.npmrc` задаёт `save-exact=true` для npm install,
|
||
Renovate использует `rangeStrategy: pin`. Обновляйте `package.json` вместе с
|
||
`package-lock.json`. Overrides в
|
||
`package.json` закрывают advisories транзитивных `multer`, `deepmerge-ts` и
|
||
`mysql2`; удаляйте их, когда родительские пакеты примут исправленные
|
||
версии. Совместимость Prisma override проверяется генерацией и SQLite migrations.
|
||
Required checks настраиваются отдельно в Gitea: наличие workflow само по себе
|
||
не блокирует merge.
|
||
|
||
Общий `tsconfig.json` включает `strict: true` и `noFallthroughCasesInSwitch`
|
||
для приложения, тестов и сборок. ESLint запрещает non-null assertions
|
||
(`value!`) и definite-assignment assertions (`field!: Type`).
|
||
Поля DTO, заполняемые вне конструктора, описываются через `declare`:
|
||
входные значения заполняет и проверяет Nest, ответные DTO описывают форму
|
||
возвращаемых объектов. `declare` не создаёт значение и не заменяет
|
||
runtime-валидацию. В обычном коде нужны инициализация или сужение типа
|
||
после проверки значения.
|
||
|
||
## Документация
|
||
|
||
- [AGENTS: правила и источники](AGENTS.md)
|
||
- [Backend architecture](.ai/architecture.md)
|
||
- [HTTP API](.ai/conventions/http-api.md), [data](.ai/conventions/data.md) и [testing](.ai/conventions/testing.md)
|
||
- [Security/auth](.ai/conventions/security-auth.md) и [integrations](.ai/conventions/integrations.md)
|
||
|
||
- [Карта проектных фактов](../docs/README.md)
|
||
- [API contract и runtime OpenAPI](../docs/architecture/http-api.md#источник-контракта)
|
||
- [Обзор поставленной системы](../docs/overview.md)
|
||
- [Архитектурные решения](../docs/adr/README.md)
|
||
|
||
Из backend runtime OpenAPI можно read-only сверить с frontend snapshot:
|
||
|
||
```bash
|
||
npm run test:openapi-contract
|
||
```
|
||
|
||
Разработка идёт напрямую по задаче пользователя. Каждый affected repo проходит
|
||
свой verify; при изменении wire contract дополнительно выполняется contract check.
|
||
Обязательного OpenSpec/SDD lifecycle нет.
|
||
|
||
## Реальная SQLite и сохранность данных
|
||
|
||
После `nvm use --silent` запускайте `npm run test:sqlite` при изменении auth,
|
||
карточных операций или DB boundary, `npm run test:e2e` — для всех HTTP suites.
|
||
Обе команды используют один runner: отдельный каталог `wealthledger-e2e-*`
|
||
в системном temp на каждую suite, один `DATABASE_URL` для migrations и Jest.
|
||
Внешний `DATABASE_URL` заменяется временным; `.env` и пользовательские данные
|
||
не изменяются. Прямой Jest без runner отклоняется до обращения к базе.
|
||
При ошибке migration/Jest или SIGINT/SIGTERM runner останавливает дочерние
|
||
процессы, удаляет свой каталог и возвращает ненулевой exit code.
|
||
Нужен доступ к локальным HTTP-портам; sandbox может его запрещать.
|
||
|
||
`prisma.config.ts` удаляет унаследованный `RUST_LOG` только в процессе Prisma CLI:
|
||
на Prisma 7.9.1 уровни `warn`/`error` вызывают пустой `Schema engine error`
|
||
при fresh migration, без переменной миграция проходит. Постоянный
|
||
`RUST_LOG=info` и обновление зависимостей не нужны. Logging приложения
|
||
настраивается отдельно. История migrations применяется штатным `migrate deploy`.
|
||
|
||
Регрессия самого запуска: `node test/sqlite-regression.cjs`. Она выполняет
|
||
три последовательных и два параллельных полных e2e, проверяет уникальность DB,
|
||
сохранность вымышленного внешнего файла, отказ migration и SIGINT/SIGTERM cleanup.
|
||
Логи остаются в каталоге из вывода. Для одной suite:
|
||
`node test/sqlite.cjs app.e2e-spec.ts`.
|
||
|
||
| Риск | Проверка реальной DB |
|
||
| --- | --- |
|
||
| Чужой пользователь | `bank-cards.e2e-spec.ts`: owner каждой карты, категории, покупки и стороны перевода |
|
||
| Повтор и конкурентный update | Те же tests: fingerprint, tombstone, версии, один refund в пределах покупки, archive/write |
|
||
| Конкурентный refresh | `app.e2e-spec.ts`: оба запроса читают прежний hash до CAS; один 200, один 401, cookie победителя работает после replay |
|
||
| Rollback и деньги | Карточные tests: откат source lock, атомарный перевод, точные суммы выше 2^53 и границы signed64 |
|
||
| Migration, restart, backup/restore | `test:durability`: прежнее приложение, новая schema, история, откат приложения и восстановление backup |
|
||
|
||
Prisma в этих suites не заменяется mocks. В refresh тесте барьер задерживает
|
||
настоящую подпись JWT, чтобы оба запроса дошли до реальной конкурентной записи.
|
||
Unit tests с mocks отдельно проверяют orchestration и не доказывают DB concurrency.
|
||
|
||
При изменении schema/migrations или сохранения данных дополнительно выполните
|
||
`npm run test:durability -- /absolute/path/to/baseline`. Он не входит в lint/verify.
|
||
Baseline — отдельная сборка до проверяемых миграций с неизменённым префиксом
|
||
migration history. Для текущей карточной схемы подходит revision
|
||
`8ec172f48eefa2237dc0c16919fbb2e726b39a65` (до добавления карт):
|
||
|
||
```bash
|
||
# Из backend; экспорт не содержит .env или локальную базу.
|
||
baseline_dir=$(mktemp -d /tmp/wealthledger-baseline-XXXXXX)
|
||
git archive 8ec172f48eefa2237dc0c16919fbb2e726b39a65 | tar -x -C "$baseline_dir"
|
||
(
|
||
cd "$baseline_dir"
|
||
nvm use --silent
|
||
npm ci
|
||
npm run build
|
||
)
|
||
nvm use --silent
|
||
npm run build
|
||
npm run test:durability -- "$baseline_dir"
|
||
```
|
||
|
||
Не подменяйте baseline текущей сборкой: runner отвергает тот же путь,
|
||
отсутствующую сборку и неподходящую migration history. Для следующей schema
|
||
выберите совместимую предыдущую revision и проверьте её API-сценарии runner.
|
||
Durability оставляет временные DB, логи и `result.json` по пути из вывода для
|
||
диагностики; он не откатывает schema пользовательской базы.
|
||
|
||
Версии и ветки: [процесс релиза](../README.md#релизы-и-ветки).
|