# Локальный запуск и проверка бэкенда
Проверено 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`.