# Премиум Водитель — PWA водителя React + Vite + TypeScript. Прод: **https://app.pptaxi.ru**. Бэкенд — модуль `payments` в Premium CRM (`https://crm.pptaxi.ru/api`, репозиторий `crm2`, `backend/app/modules/payments/`). ## Как поднять локально ```bash npm install npm run dev # http://localhost:5174 ``` Отдельно поднимается бэкенд `crm2` на **127.0.0.1:8000** (тот же порт, что в `backend/Dockerfile`). Больше настраивать нечего: dev-сервер сам подставляет `VITE_API_BASE=/api` и проксирует `/api` на бэкенд — см. `vite.config.ts`. **Почему через прокси, а не «разрешить localhost в CORS бэкенда».** Прокси делает запрос ОДНОГО происхождения со страницей (`http://localhost:5174/api/…`), поэтому CORS в разработке не участвует вообще. Боевой список origin'ов на бэкенде остаётся закрытым, а `CORS_ALLOW_LOCALHOST=true` в `backend/.env` для этого фронта включать не надо. (Раньше рецепт с `CORS_ALLOW_LOCALHOST` тут и не работал: прокси не было, и фронт по умолчанию уходил на боевой домен, а не на локальный бэкенд.) Сервис-воркер в режиме разработки не регистрируется (`devOptions` у `vite-plugin-pwa` по умолчанию выключены) — старый кэш прод-версии в дев не лезет. ### Бэкенд не на `127.0.0.1:8000` ```bash VITE_API_TARGET=http://127.0.0.1:8090 npm run dev ``` В PowerShell инлайн-префикса `VAR=... команда` нет — переменную ставят отдельно: ```powershell $env:VITE_API_TARGET="http://127.0.0.1:8090"; npm run dev ``` Имя переменной то же, что в CRM (`crm2/frontend/vite.config.ts`), — рецепт один на оба фронта. ### Ходить в API напрямую, мимо прокси `.env.local` (в git не попадает): ``` VITE_API_BASE=http://127.0.0.1:8000/api ``` Своя настройка приоритетнее дев-дефолта — специально, чтобы `npm run dev` её не перебивал. Для похода в чужой origin бэкенду понадобится `CORS_ALLOW_LOCALHOST=true` в `backend/.env` и рестарт. > ⚠️ `crm.pptaxi.ru` — боевой терминал Т-Банка и боевая касса АТОЛ. Пополнение, > сделанное «просто посмотреть», — это реальные деньги водителя, реальный чек > 54-ФЗ и реальная проводка в леджере. Поэтому прокси по умолчанию смотрит в > локальный бэкенд, а не в прод. ## Проверки ```bash npx tsc -b # типы npm test # vitest run ``` `npx tsc --noEmit` **без `-b`** здесь всегда зелёный и ничего не значит: корневой `tsconfig.json` — solution-файл (`files: []` + `references`), под него не попадает ни один файл (`tsc --noEmit --listFiles` печатает пустоту). Типы живут в `tsconfig.app.json` (`src`) и `tsconfig.node.json` (конфиги + `dev-proxy.test.ts`), и до обоих добирается только `tsc -b`. ## Сборка ```bash npm run build # tsc -b && vite build → dist/ ``` Дев-дефолт `VITE_API_BASE=/api` подставляется только при запущенном dev-сервере и в бандл не попадает: в собранном `assets/index-*.js` лежит `https://crm.pptaxi.ru/api` (проверено сборкой). Поэтому `VITE_API_BASE` боевой сборке задавать не обязательно — без неё `src/api/client.ts` подставляет ровно этот адрес. > ⚠️ `npm run preview` поднимает **уже собранный** бандл, а база в нём > абсолютная: запросы уходят на боевой `crm.pptaxi.ru` мимо прокси dev-сервера. > Смотреть локальные правки — только `npm run dev`. Куда выкладывается `dist/` — в плане проекта: `../../docs/superpowers/plans/2026-06-18-premium-driver-pwa.md`, раздел «Deployment notes»: статика на `app.pptaxi.ru`, Caddy с фолбэком на `index.html`. ## Обкатка: кто может войти и как смотреть чужой экран Приложение выложено на боевой домен, а обкатывается интерфейс. Поэтому на время обкатки включены две вещи. ### 1. Вход только по списку номеров Переменная бэкенда (`crm2/backend/.env`, читается в `app/config.py`): ``` DRIVER_APP_ALLOWED_PHONES=+7 (914) 123-45-67, 79990001122 ``` Разделители — запятая, точка с запятой, перенос строки (пробел разделителем не считается: номер вписывают в привычном виде). Сверяются последние 10 цифр. **Пусто — гейт выключен, вход открыт всем водителям.** Проверка стоит в единственной воронке выдачи токена (`modules/payments/driver_auth.create_driver_token_for`), поэтому закрыты сразу все пять путей входа: код в бот, кнопка «Войти через Telegram», колбэки своего бота и два аварийных отката на n8n. Бот отвечает человеку текстом, остальные пути — 403. Логика гейта: `modules/payments/login_gate.py`. ### 2. Просмотр от лица водителя (сотрудник) На экране входа внизу — «Вход для сотрудника» (`/dev`): логин и пароль **учётки CRM**, при включённой двухфакторке спросит код. Дальше поиск по ФИО, телефону или id — и приложение открывается от лица выбранного водителя. * Токен помечен claim'ом `imp`, живёт 8 часов (обычный водительский — 30 дней). * Гейт — `is_admin` в CRM. Каждая выдача пишется в аудит (`driver_app.impersonate`). * Кнопка «Отозвать доступ» в CRM гасит и просмотр сотрудника — поколение общее. * Гейт обкатки на этот путь не распространяется: смотреть надо как раз на водителей, которых в allowlist нет. Пока просмотр действует, сверху на всех экранах висит плашка «Смотрю как …» со ссылкой на вкладку **«Отладка»** (`/debug`, водителю недоступна): * от чьего лица открыт экран и кнопка сменить водителя; * `driver_id`, режим, сотрудник, когда истекает токен, адрес API, коммит и время сборки, состояние сети; * журнал последних 40 ответивших запросов (метод, путь, статус, миллисекунды); * сброс кэша сервис-воркера с перезагрузкой — лечит «правка не приехала»; * выходы: из просмотра, из учётки сотрудника. ### Деньги под просмотром: учебный платёж Пополнение под `imp`-токеном **не идёт в банк**. Проверки суммы, категории и расчёт комиссии настоящие, а вместо Init на терминале заводится учебный заказ в Redis (`modules/payments/mock_pay.py`, префикс `mock-`, час жизни). Экран `/mock-pay/:orderId` даёт выбрать, что «ответит» банк — оплачено, отказ, истёкший счёт, возврат, — и настоящий экран оплаты разбирает эти статусы своим боевым кодом. В `driver_payments` не попадает ни строки, чек 54-ФЗ не пробивается.