codex-bot e03fad622b
Backend verify / commits (push) Skipped
Backend verify / commits (pull_request) Failing after 32s
Backend verify / security (pull_request) Skipped
Backend verify / verify (pull_request) Skipped
Backend verify / security (push) Successful in 10s
Backend verify / verify (push) Failing after 6m21s
fix: align password policy to 8-72 printable ASCII on register (WealthLedger/backend#60) (#109)
2026-10-16 10:00:47 +03:00
2026-08-13 19:31:43 +03:00
2026-08-14 17:12:15 +03:00
2026-09-13 15:05:38 +03:00
2026-10-12 23:19:12 +03:00

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
npx prisma generate
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

Он запускает генерацию Prisma client, 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-проверки выполните обе команды. Тяжёлый synthetic benchmark с 300 000 операций и 1 000 000 свечей запускается отдельно командой npm run test:performance: его wall-clock budget зависит от выделенного runner CPU и не является функциональным merge gate.

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 запускает commits → security → verify последовательно на PR; на push в develop/main — security → verify. Рабочие ветки проверяются после открытия PR, без дублирующего push-запуска. Изменение заголовка PR повторяет только проверку Conventional Commits; security и verify на metadata-only событии не запускаются. Новый запуск отменяет устаревший для того же PR/ветки и типа события (требуется Gitea 1.26+). Ежедневно в 05:47 UTC выполняется только security. Job verify запускает npm ci и npm run verify (генерация Prisma client внутри gate, один Nest build), затем функциональный node test/sqlite.cjs --all на уже собранных артефактах. Используется только checkout backend и его зависимости; OpenAPI генерируется во время проверки, SHA записывается в лог. Успешный unit gate не заменяет SQLite/e2e; ошибка любой suite завершает job с FAIL.

Job security сканирует всю Git-историю Gitleaks 8.30.1 с полностью скрытыми значениями, блокирует любой runtime advisory через npm audit --omit=dev --audit-level=low и high/critical findings полного дерева через npm audit --include=dev --audit-level=high. Moderate advisory sprintf-js остаётся только в Jest coverage tooling; исправленной версии пока нет, а предложенный npm downgrade ts-jest несовместим с текущим test stack. .gitleaksignore перечисляет только проверенные исторические совпадения: детерминированные fixtures, placeholder и badge из Nest starter. Новые коммиты этими исключениями не покрываются.

Имена jobs commits, security, verify сохранены, но Gitea включает имя workflow в статус. При внедрении администратор должен заменить required checks для защищённых веток на Backend verify / commits (pull_request), Backend verify / security (pull_request) и Backend verify / verify (pull_request), удалив старые имена и push-статусы, если они требовались. Все три PR-проверки должны оставаться обязательными. Публикация образов по тегам остаётся в image.yml. Последовательность jobs ограничивает один запуск; для ограничения нагрузки всего сервера настройте runner.capacity: 1 у общего runner и учтите остальные зарегистрированные runners. Конфигурация runner управляется вне этого репозитория.

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 не включены.

Рост baseline — изменение quality gate: он требует отдельного обоснования и review затронутого поведения. При рефакторинге сохраняйте или сокращайте 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 в корень репозитория атомарно: через временный файл и rename, без частичной записи при сбое. npm run openapi:check только сравнивает runtime schema с файлом и завершается с ошибкой при расхождении, не перезаписывая его. Сервер и 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 suites, проверяет уникальность DB, сохранность вымышленного внешнего файла, отказ migration и SIGINT/SIGTERM cleanup. Полный e2e набор остаётся в npm run test:e2e. Логи остаются в каталоге из вывода. Для одной suite: node test/sqlite.cjs app.e2e-spec.ts. Crash-регрессия в tbank-operations-sync.e2e-spec.ts загружает классы из dist/, поэтому перед адресным запуском этой suite выполните npm run build, иначе child/dist может расходиться с parent/src.

Риск Проверка реальной 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 — принятые релизы. Обычная работа: issue-linked ветка от актуального develop, PR в develop; общие правила — в Engineering Workflow по ссылке из AGENTS. Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. Commits и PR titles — Conventional Commits со ссылкой на Issue (кроме release), включая 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 не включены.

S
Description
No description provided
Readme
2.2 MiB
Languages
TypeScript 95.8%
JavaScript 4.1%
Dockerfile 0.1%