Приложение обкатывается на боевом контуре, поэтому интерфейс надо проверять на живых данных — а живые данные лежат у настоящих водителей. * «Вход для сотрудника» (/dev) — логин учётки CRM, поддержан второй шаг 2FA; дальше поиск водителя по ФИО/телефону/id и просмотр его экранов. * Плашка «Смотрю как …» над всеми экранами: интерфейс в этом режиме неотличим от водительского, и чужой баланс легко принять за свой. * Вкладка «Отладка» (/debug, водителю недоступна): смена водителя, driver_id, режим, срок токена, адрес API, коммит и время сборки, журнал последних 40 ответивших запросов, сброс кэша сервис-воркера. * Пополнение под просмотром идёт в учебный контур: экран /mock-pay/:orderId выбирает, что «ответит» банк, а настоящий экран оплаты разбирает эти статусы своим боевым кодом. Ни рубля, ни чека 54-ФЗ. Заодно доезжает незакоммиченная работа прошлой сессии: дев-прокси в vite.config.ts, его тест и разобранный .env.example. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
148 lines
8.9 KiB
Markdown
148 lines
8.9 KiB
Markdown
# Премиум Водитель — 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-ФЗ не пробивается.
|