# Локальный запуск и проверка бэкенда Проверено 22.06.2026: схема применяется, все 3 e2e-набора зелёные, HTTP-слой (auth/guard/валидация/throttler/Swagger/readiness) работает против живого PostgreSQL. Контейнер сбрасывает ФС между задачами — этот runbook воспроизводит окружение с нуля. ## 1. PostgreSQL 16 на порту 5433 (как в `.env`) Системный кластер Ubuntu уже инициализирован; переключить порт на 5433 и поднять: ```sh sed -i 's/^[# ]*port = .*/port = 5433/' /etc/postgresql/16/main/postgresql.conf pg_ctlcluster 16 main start pg_isready -h 127.0.0.1 -p 5433 ``` (PostgreSQL отказывается стартовать под root — `pg_ctlcluster` сам дропается в пользователя `postgres`.) ## 2. БД, схема, роли (суперпользователь) ```sh su postgres -c "createdb -p 5433 stankin_app" # extensions/RLS/роли app_rw·app_ro требуют суперпользователя: su postgres -c "psql -p 5433 -d stankin_app -v ON_ERROR_STOP=1 -f db/schema.sql" su postgres -c "psql -p 5433 -d stankin_app -v ON_ERROR_STOP=1 -f db/02-app-role.sql" # app_user + страны # уже развёрнутая БД (до сентября 2026) — догнать схему правок анкеты; свежей установке не нужно: su postgres -c "psql -p 5433 -d stankin_app -v ON_ERROR_STOP=1 -f db/03-anketa-2026-09.sql" ``` Приложение подключается под `app_user` (член `app_rw`, НЕ суперпользователь) → RLS применяется. RLS-контекст ставится в транзакции: `SET LOCAL app.account_id` / `SET LOCAL app.role` (см. `src/prisma/prisma.service.ts`); политики на `person/application/document/payment`. ## 3. Prisma client ```sh npx prisma generate ``` ## 4. e2e (сервисный слой, против живой БД) Наборы рассчитаны на ЧИСТУЮ БД — сбрасывать перед каждым: ```sh RESET() { su postgres -c "psql -p 5433 -d stankin_app -q -f db/99-reset-dev.sql"; } RESET; npm run e2e # этап 1: auth/RLS/заявки/документы(AV,типы)/сброс пароля/инвайты/блокировка/почта RESET; npm run e2e:stage2 # этап 2: зачисление/студкарта/общежитие/обращения/виза/платежи/изоляция данных RESET; npm run e2e:stage3 # этап 3: напоминания визы/паспорта, локализация писем RU/EN/中文, идемпотентность RESET; npm run e2e:http # HTTP-слой: весь REST API сквозь guard/pipe/filter — auth, ролевая авторизация, RLS, все операционные потоки ``` Без сброса этап 2 падает с `P2002 unique email` (хардкод-адреса пересекаются с этапом 1) — это ограничение тест-данных, не дефект бэкенда. ## 5. HTTP-сервер и smoke-проверка `npm run e2e:http` автоматизирует полную HTTP-проверку (тест сам поднимает Nest через общую `configureApp` из `src/app.config.ts` — единый источник конфигурации с `main.ts` — и бьёт по реальному HTTP). Ручной вариант одним сеансом ниже. Фоновый процесс НЕ переживает между отдельными вызовами — старт + проверки + стоп в одном скрипте, все fd перенаправлены (иначе bash-инструмент висит на pipe): ```sh RESET nohup node dist/src/main.js > /tmp/nest.log 2>&1 < /dev/null & SV=$! trap 'kill $SV' EXIT for i in $(seq 1 30); do curl -sf -o /dev/null http://127.0.0.1:3000/api/v1/health && break; sleep 1; done B=http://127.0.0.1:3000/api/v1 curl -s $B/health # {"status":"ok"} curl -s $B/health/ready # {"status":"ready","db":"up"} curl -sI $B/docs # 200, Swagger UI # полный auth-флоу: dev-почта (MemoryMailService) пишет токен в лог → # register → grep 'token=' /tmp/nest.log → verify-email → login (JWT) → GET /auth/me (Bearer) ``` Проверено: register 201; verify 201; login → JWT; `/auth/me` 200 (account+person); без токена 401; register без `consent` 400 (+кастомное 152-ФЗ-сообщение); лишнее поле → `forbidNonWhitelisted` 400; throttler на `/auth/login` → 429 после ~6 запросов; единый error-формат с `requestId`; заголовок `X-Request-Id` на каждом ответе. ## Прод (вне scope локалки — нужны ВАС-решения) `docker-compose.yml`, `deploy/` (nginx+TLS, compose.prod, init-letsencrypt), `.env.example`. Стоп-условия: реальные ключи 1С/МВД/эквайринг/SMTP/S3/ClamAV, KMS для `PDN_ENC_KEY`/`JWT_SECRET`, Redis для throttler, прод-деплой+TLS. Транспорт почты: `MAIL_TRANSPORT=smtp` → `SmtpMailService` (иначе dev `MemoryMailService` с логом токена — только для разработки). ## Фронт + бэкенд вместе (локально) 1. Поднять БД и бэкенд (см. выше), важно разрешить CORS со статики: ``` CORS_ORIGIN='*' PORT=3000 node dist/src/main.js ``` (в проде — конкретный origin сайта, не `*`). 2. Отдать статику сайта `/home/claude/site` любым статик-сервером (порт, напр., 8080). 3. База API на фронте определяется по порядку: `window.STANKIN_API_BASE` → `` → дефолт `http://localhost:3000/api/v1`. Для нестандартного адреса добавить в `lk.html` (и др. страницы кабинета) до `assets/api.js`: ```html ``` 4. Вход в кабинет: реальные email+пароль → вход через API; «гость»/пустой пароль → демо-режим (fallback). ### Live-E2E кабинета (Playwright против запущенного бэкенда) Сид `seed_front.js` (admin + активная программа + подтверждённый абитуриент) → статик-сервер → `pw_lk.js` гоняет реальный UI: вход → заявка → обработка ДМС → зачисление → платёж → скачивание квитанции. Все шаги — зелёные. Скриншоты в `outputs/e2e-*.png`.