# Netrunner — фронтенд SPA-клиент (React 19 + TypeScript + Vite) для Netrunner Control Plane. Собирается в статику и раздаётся самим бэкендом ([`../src/api.rs`](../src/api.rs) — `ServeDir` с SPA-фолбэком на `index.html`), поэтому в проде отдельного веб-сервера для фронта нет. ## Экраны (`src/*.tsx`) | Файл | Роут | Что делает | |---|---|---| | `Auth.tsx` | `/` | Вход: Telegram Login Widget, генерация/логин Ghost-seed (с обязательным скачиванием + подтверждением сохранения seed перед переходом дальше). Хранит терминальный лог событий для киберпанк-эстетики. | | `Profile.tsx` | `/profile` | Личный кабинет: баланс, статус подписки, покупка тарифа через TON Connect, реферальная ссылка. Также цель WebApp-ссылок из бота («Личный Кабинет»/«Тарифы»/«Синдикат»). | | `HackGame.tsx` | `/hack` | Обёртка над npm-пакетом `cyberhack` (мини-игра). Server-authoritative: сначала `POST /users/hack/start` за доской, сгенерированной сервером, по завершении партии шлёт путь кликов на `POST /users/hack/result` — сервер сам считает очки. | | `AdminDashboard.tsx` | `/admin` | Список всех VPN-нод (`GET /admin/nodes`, любой статус) + форма добавления ноды + force-restart/удаление. Доступ ограничен `admin_guard` на бэкенде; сам роут на фронте не проверяет роль (полагается на то, что API откажет не-админу `401/403`). | `App.tsx` — роутинг (`react-router-dom`), провайдер `TonConnectUIProvider` и мост `useBootstrapTokenExchange`: если в URL есть `#token=` (пришли из бота), сразу меняет его на cookie-сессию через `POST /auth/exchange` и чистит URL. `api.ts` — единая обёртка `apiFetch` над `/api/v1/*`: всегда с cookie (`credentials: "same-origin"`), при первом `401` тихо пробует `POST /auth/refresh` и повторяет запрос один раз, при провале — редирект на `/`. ## Аутентификация на фронте Сессия — только HttpOnly-cookie (`nrxp_token`/`nrxp_refresh`), которую полностью контролирует сервер; фронт её не читает и не хранит нигде сам (ни `localStorage`, ни JS-видимая cookie). Раньше `api.ts` пытался читать `nrxp_token` через `document.cookie` для простановки в `Authorization: Bearer`, но сервер ставил её `HttpOnly` — то есть JS физически не мог её увидеть, и все "авторизованные" запросы уходили без токена вообще. Подробности серверной части — в [`../src/modules/auth/README.md`](../src/modules/auth/README.md). ## Разработка ```bash pnpm install pnpm dev # dev-сервер Vite; бэкенд отдельно на :8080 (см. VITE_API_BASE) pnpm build # tsc -b && vite build -> dist/ pnpm lint ``` Переменные окружения (`.env.production` / Vite `import.meta.env`): `VITE_API_BASE` (по умолчанию `/api/v1`), `VITE_BOT_USERNAME`.