WealthLedger backend
NestJS API WealthLedger. Backend владеет auth, SQLite-данными и server-side интеграциями. Потребители используют публичный HTTP API и OpenAPI artifact; их репозитории не нужны для разработки, сборки и проверок backend.
Быстрый старт
Требуется Node 24 (.nvmrc). Создайте .env из примера, замените placeholder
секреты и установите зависимости строго из lockfile:
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: он вводится пользователем и хранится в зашифрованном
виде.
Контейнерный image
Dockerfile имеет targets development (Nest watch, generated Prisma client и Linux native modules)
и production (compiled Node process от UID 1000, Prisma CLI/migrations и T-Bank proto assets). Node соответствует .nvmrc.
Build context ограничен .dockerignore; .env, local DB и host node_modules
не попадают в image.
docker build --target development -t wealthledger-backend:development .
docker build --target production -t wealthledger-backend:production .
nvm use --silent
npm run test:container -- wealthledger-backend:production
Gitea image.yml запускается на защищённом release tag vX.Y.Z, проверяет
package/lock version и canonical gates. Runner ubuntu-latest должен иметь
Docker/buildx. Образы хранятся в Nexus:
nexus.ksv741.keenetic.pro/wealthledger/backend. Dedicated Nexus writer
задаётся Actions secrets NEXUS_USERNAME/NEXUS_PASSWORD; server использует
отдельную read-only identity. Docker endpoint и token realm должны использовать
HTTPS; в docker-hosted включается запрет redeploy. Прежние Gitea secrets
сохраняются на время перехода и не передаются в Nexus.
Публикуются semver tags, без latest; существующие release tags не перезаписываются. Production выбирает опубликованный version tag.
Compose topology принадлежит workspace, эксплуатационная последовательность —
документация,
страница architecture/deployment.md.
Проверка публикации требует настроенного registry/runner; локальный build
не доказывает package permissions.
Проверка
Единый completion gate не изменяет versioned-файлы:
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-валидацию. В обычном коде нужны инициализация или сужение типа
после проверки значения.
SonarQube
Проект wealthledger-backend на https://sonarqube.ksv741.keenetic.pro
использует sonar-project.properties. Анализируются исходники src; unit specs
и HTTP specs учитываются как тесты. Внешние T-Bank proto contracts исключены,
generated Prisma client находится за пределами sonar.sources.
npm run verify запускает unit tests с покрытием один раз. Jest штатно создаёт
coverage/lcov.info; scanner импортирует этот отчёт. Покрытие HTTP/e2e tests
в него не входит, сами HTTP suites остаются обязательным отдельным шагом CI.
Создайте в SonarQube проект с ключом wealthledger-backend, а в Gitea → backend →
Settings → Actions → Secrets добавьте SONAR_TOKEN с правом анализа этого проекта.
Токен не сохраняется в файлах. Community Edition 9.9.8 получает анализ только
при push в develop, после успешных verify и SQLite/e2e checks. CI ожидает
Quality Gate; отрицательный результат или ошибка scanner завершает job с ошибкой.
PR и другие ветки проходят штатные проверки без отправки анализа.
Переименование основной ветки SonarQube не требуется; sonar.branch.name не задаётся.
Runner должен поддерживать Docker actions и иметь доступ к GitHub, Docker Hub
и HTTPS-адресу SonarQube с действующим сертификатом для этого имени.
Официальный Docker action закреплён на commit версии 2.3.0, как в frontend.
tsconfig.sonar.json содержит совместимые со старым анализатором настройки
модулей и decorators с target ES2022, без новых compiler options приложения.
Штатная компиляция приложения остаётся на ES2023.
После обновления сервера пересмотрите scanner и необходимость отдельного tsconfig.
Локально после nvm use --silent, npm ci и генерации Prisma client выполните
npm run verify, затем sonar-scanner с совместимым SonarScanner CLI и
SONAR_TOKEN в окружении. Первый запуск CI после merge должен подтвердить
загрузку отчёта и покрытия: локальные проверки не доказывают доступность сервера.
Документация
Генерация без сервера сверяется с OpenAPI настроенного приложения:
npm run test:openapi-contract
Генерация OpenAPI
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 (до добавления карт):
# Из 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:. Версии вручную не повышай.
- Получи
main,developи теги:git fetch origin --tags. Убедись, чтоdevelopсодержит последний релиз. Создайrelease/<name>от проверенногоdevelop;nvm use --silent,npm ci. Hotfix начинается отmainвrelease/hotfix-<name>и проходит тот же процесс. - На чистой ветке выполни
npm run release:preview, просмотри версию/changelog, затемnpm run release:prepare. Команда создаётchore(release): X.Y.Z, обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии. - Проверь diff и штатные gates этого репозитория. Отправь только release-ветку,
открой PR в
mainс заголовкомchore(release): X.Y.Z. После зелёныхverify,security,commitsвыполни fast-forward-only merge. Еслиmainизменился, согласуй историю и повтори проверки кандидата. - Получи удалённый
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. - Верни 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 не включены.