Files
backend/README.md
T
ksv741 32c12fb10d
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m28s
Conventional commits / commits (pull_request) Successful in 25s
Backend security / security (push) Successful in 6s
Backend verify / verify (push) Successful in 2m16s
feat: generate OpenAPI without peer repositories
2026-09-15 20:45:39 +03:00

212 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WealthLedger backend
NestJS API WealthLedger. Backend владеет auth, SQLite-данными и server-side
интеграциями. Потребители используют публичный HTTP API и OpenAPI artifact;
их репозитории не нужны для разработки, сборки и проверок backend.
## Быстрый старт
Требуется 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 на новых базах
и соответствие генерируемого OpenAPI настроенному приложению. Для полной 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. Используется только checkout
backend и его зависимости; OpenAPI генерируется во время проверки, SHA записывается в лог.
Успешный 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)
- [Генерация OpenAPI](#генерация-openapi)
Генерация без сервера сверяется с OpenAPI настроенного приложения:
```bash
npm run test:openapi-contract
```
## Генерация OpenAPI
```bash
nvm use --silent
npm run openapi:generate
```
Команда собирает backend и записывает `openapi.json` в корень репозитория.
Сервер и HTTP-порт не запускаются; Nest preview читает metadata без создания
провайдеров и подключения к БД/T-Bank. Настроенный `.env` и реальные секреты
не нужны. `openapi.json` исключён из Git и каждый раз создаётся из DTO/Swagger
metadata; передай полученный artifact потребителю отдельно.
`npm run test:openapi-contract` собирает backend и сравнивает результат генератора
с runtime schema настроенного приложения, используя временный каталог и SQLite.
`npm run test:e2e` также сначала собирает backend. Изменение публичного API
проверяй relevant HTTP tests: без versioned snapshot сравнение не отслеживает
breaking changes относительно предыдущего выпуска.
Разработка идёт напрямую по задаче пользователя; OpenSpec/SDD — по явному запросу.
## Реальная 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 пользовательской базы.
## Релизы
Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые
релизы. Работа: `codex/<task>` от актуального `develop`, PR в `develop`.
Обычные PR допускают squash; release и обратная синхронизация сохраняют историю.
Commits и PR titles — Conventional Commits, включая `spec:`. Версии вручную не повышай.
1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что
`develop` содержит последний релиз. Создай `release/<name>` от проверенного
`develop`; `nvm use --silent`, `npm ci`. Hotfix начинается от `main` в
`release/hotfix-<name>` и проходит тот же процесс.
2. На чистой ветке выполни `npm run release:preview`, просмотри версию/changelog,
затем `npm run release:prepare`. Команда создаёт `chore(release): X.Y.Z`,
обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи
guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии.
3. Проверь diff и штатные gates этого репозитория. Отправь только release-ветку,
открой PR в `main` с заголовком `chore(release): X.Y.Z`. После зелёных
`verify`, `security`, `commits` выполни fast-forward-only merge. Если `main`
изменился, согласуй историю и повтори проверки кандидата.
4. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией
manifest/lockfile. Создай аннотированный `vX.Y.Z` именно на этом SHA:
`git tag -a vX.Y.Z <release-sha> -m "Release X.Y.Z"`, затем отправь только
`git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`.
5. Верни release-коммит через PR `main → develop` без squash/rebase; проверь,
что `develop` содержит тег. Опубликованные теги не перемещай. При сбое после
merge создай отсутствующий тег без нового bump; до merge исправь и проверь
существующий кандидат без повторного повышения версии.
`feat:` повышает minor, `fix:`/`perf:` и прочие разрешённые типы — patch;
`!`/`BREAKING CHANGE:` — major, включая версии до 1.0. `chore(release):`
исключён из рекомендации и changelog. Конфигурация — `.release-it.json`.
В Gitea для `main`/`develop` должны быть запрещены прямой/force push и обход
администратором, обязательны `verify`, `security`, `commits`; теги `v*` защищены.
Workflow сам не включает эти настройки. Отдельное review approval для одного
владельца не требуется. Npm publishing, deploy и Gitea Releases не включены.