feat(proj): init

This commit is contained in:
vl.arkhangelskii
2026-09-21 04:06:43 +03:00
commit c956b94983
1076 changed files with 50876 additions and 0 deletions
@@ -0,0 +1,69 @@
# 2026-09-20 — Вход через WebView + Telegram Login Widget
## Запрос
Реализовать нативный вход: открыть кабинет в WebView и забрать JWT через
JS-канал. Бэкенд не менять.
## Почему не «просто открыть страницу»
Telegram Login Widget в обычном браузере работает через iframe + `data-onauth`.
В Android/iOS WebView этот путь ломается:
- popup (`window.open` на `oauth.telegram.org`) не возвращает `opener`;
- iframe режется third-party cookies и User-Agent с маркером `; wv`;
- callback `onTelegramAuth` просто не вызывается.
Поэтому в WebView виджет переключается на **redirect-режим** (`data-auth-url`),
а Flutter забирает уже готовый JWT, который кабинет и так кладёт в
`localStorage.ppm_session_jwt`. API (`POST /api/auth/telegram`) не трогали.
## Поток
1. Экран входа на iOS/Android показывает «Войти через Telegram».
2. Открывается fullscreen WebView на `{cabinet}/login`.
3. Кабинет видит канал `window.PpmAuth` → ставит виджету `data-auth-url=/login`.
4. Пользователь логинится в Telegram; редирект приходит на `/login?id&hash&…`.
5. `telegramPayloadFromQuery` собирает payload, кабинет зовёт тот же
`loginWithTelegram` / `POST /api/auth/telegram`.
6. `setSession` пишет JWT в `localStorage` и зовёт `PpmAuth.postMessage`.
7. Flutter парсит сообщение, закрывает WebView, проверяет токен через
`GET /api/me`, кладёт сессию в `SharedPreferences`.
Запасные пути, если кабинет ещё не задеплоен с мостом:
- JS в WebView хукает `localStorage.setItem('ppm_session_jwt')` и поллит ключ;
- на Windows/web WebView нет — форма «вставить токен»;
- «Демо-режим» по-прежнему без сети.
## Что изменилось
### Web (не API)
- `web/src/auth/telegramRedirect.ts` — разбор query и `postTokenToNativeApp`.
- `TelegramLoginButton``data-auth-url` только если есть `window.PpmAuth`.
- `LoginPage` — автологин, если в URL уже лежит telegram-payload.
- `setSession` — после записи токена шлёт его в канал.
### Mobile
- `TelegramLoginScreen` (`webview_flutter`): канал `PpmAuth`, Chrome UA без
`; wv`, third-party cookies на Android, `tg://` уходит во внешнее приложение,
кнопка «назад» после oauth.telegram.org.
- `resolveCabinetLoginUri` всегда открывает `/login`.
- `INTERNET` в main `AndroidManifest` (раньше был только в debug).
- iOS: `LSApplicationQueriesSchemes` для `tg` / `telegram`.
- `PPM_WEB_URL` по умолчанию = `PPM_API_BASE_URL` (один origin за nginx).
## Тесты
- `resolveApiBaseUrl` / `resolveCabinetLoginUri` / парсер канала.
- виджет: инжектированный launcher отдаёт JWT → `GET /api/me` → signed in.
- 32 теста приложения, 102 в Widgetbook, `flutter analyze` чистый.
## Запуск против живого кабинета
```powershell
cd mobile
.\run.ps1 -Device windows # на Windows будет форма токена
# на телефоне:
.\run.ps1 -ApiBaseUrl https://<домен-кабинета>
```
Нужен публичный HTTPS и BotFather `/setdomain` на этот домен — как для веба.