Use owner enums for error and T-Bank protocol values with ESLint regression checks. Preserve open wire fallbacks and keep protocol imports independent of DI and Prisma. Verified npm ci, verify, 226 unit tests, 262 isolated e2e tests and unchanged OpenAPI.
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.personal.ksv741.ru/docker-hosted/wealthledger/backend. Пользователь wealthledger-ci
задаётся Actions secrets NEXUS_USERNAME/NEXUS_PASSWORD; эта же учётная
запись используется сервером для загрузки образов. 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-валидацию. В обычном коде нужны инициализация или сужение типа
после проверки значения.
ESLint
npm run lint проверяет весь authored TypeScript и JavaScript, включая scripts
и configs. npm run lint:fix (также npm run format) применяет безопасные
автоисправления. Форматирование принадлежит ESLint Stylistic: два пробела,
одинарные кавычки и точки с запятой; Prettier не используется.
Профиль: ESLint recommended, typescript-eslint recommendedTypeChecked, Node, NestJS typed, Jest, import-x с TypeScript resolver, SonarJS, RegExp, Security, Perfectionist recommended-natural и Stylistic customize. NestJS проверяется в production TS, Jest — в specs/e2e; generated Prisma, dist и coverage исключены. DI imports сохраняют runtime metadata.
Исключения конфигурации: private backend не публикуется в npm; TS imports разрешает import-x вместо Node resolver; IsIn проверяет domain unions. Автосортировка объектов, классов, модулей и декораторов отключена, поскольку порядок может влиять на runtime. Остальные точечные исключения объясняют false positives непосредственно в коде.
eslint-suppressions.json — штатный ESLint baseline только для существующих
SonarJS cognitive-complexity, no-nested-conditional и no-nested-template-literals.
Это отложенный refactor, а не исправленные нарушения. Baseline ограничен
файлами и количеством сообщений; новые файлы и превышение количества блокируют
lint. Он не защищает от усложнения уже учтённой функции при прежнем количестве
сообщений — это остаётся предметом review. При сокращении долга обновляйте
baseline командой npx eslint . --prune-suppressions; не пересоздавайте его
для обхода новых ошибок. Security, TypeScript и Jest в baseline не включены.
SonarQube
Проект wealthledger-backend на https://sonarqube.personal.ksv741.ru
использует 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 не включены.