Files
netrunner-backend/frontend
nineap 375b333978 Ролевая админка (Owner/Moderator/Partner) + оплата через @CryptoBot
Ролевая система вместо привязки к Telegram
----------------------------------------
Раньше единственный способ получить админ-доступ — admin_guard, пускавший
по ADMIN_TG_ID или role=="admin" (без сеттера для роли). Заменяет на
Owner/Moderator/Partner:

- migrations/0005_roles_and_partners.sql — username/password_hash на users
  (NULL у обычных Ghost/Telegram-юзеров, тот же паттерн, что и у seed_hash/
  tg_id), can_manage_nodes, commission_percent, partner_code,
  referred_by_partner_id; новые таблицы partner_commissions и
  withdrawal_requests.
- auth/guard.rs — admin_guard заменён на owner_guard/staff_guard/
  node_manage_guard/partner_guard; ADMIN_TG_ID убран из ApiState и main.rs.
- auth/service.rs — hash_password/login_staff со случайной Argon2-солью на
  каждого юзера (не общий фиксированный ARGON_SALT, который годится для
  высокоэнтропийных seed-логинов, но не для человеческих паролей).
- POST /api/v1/auth/admin/login — вход модератора/партнёра/овнера паролем,
  переиспользует существующий issue_session().
- --create-owner CLI-флаг (main.rs) со скрытым вводом пароля (rpassword) —
  вместо ручного SQL для создания первого владельца с VPS-консоли.
- src/modules/staff/ — эндпоинты создания модераторов/партнёров, переключения
  can_manage_nodes, списка заявок на вывод и их approve/reject/paid.
- Комиссия партнёра начисляется в BillingService::confirm_payment отдельным
  шагом после существующей рефералки, пересчитывается через
  get_plan_price(invoice.plan_name, invoice.currency) — НЕ через
  invoice.amount напрямую, потому что у провайдеров разная внутренняя
  единица (TON — nanoTON+2% буфер, CryptoBot — фиатные центы напрямую).
- bot.rs — /start p_XXXXXX привязывает покупателя к партнёру по коду
  (first-wins, до существующих веток numeric-tg_id-рефералки и кода входа).
- frontend/ — AdminLogin/AdminLayout/StaffPage/WithdrawalsPage/EarningsPage
  поверх существующего Vite-админ-фронта (без отдельного проекта).

Оплата через @CryptoBot
------------------------
- modules/billing/providers/cryptobot.rs — новый PaymentProvider (Crypto Pay
  API), плюс попутно исправлены два реальных бага в существующем коде:
  confirm_payment был жёстко завязан на TON-провайдера при любом инвойсе, и
  InvoiceRecord не хранил currency, из-за чего TON-курс молча применялся ко
  всем провайдерам вместо родной валюты инвойса.
- bot.rs — экран выбора способа оплаты для CryptoBot-инвойсов.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 22:53:02 +07:00
..
2026-04-20 14:30:18 +07:00
2026-04-19 17:15:50 +07:00
2026-04-25 18:21:24 +07:00
2026-04-20 14:30:18 +07:00
2026-04-19 17:15:50 +07:00
2026-04-19 17:15:50 +07:00
2026-04-19 17:15:50 +07:00
2026-04-19 17:15:50 +07:00
2026-04-28 19:55:11 +07:00

Netrunner — фронтенд

SPA-клиент (React 19 + TypeScript + Vite) для Netrunner Control Plane. Собирается в статику и раздаётся самим бэкендом (../src/api.rsServeDir с 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.

Разработка

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).

Юзернейм бота (для Telegram Login Widget и реферальной ссылки) не build-time переменная — этот SPA собирается один раз в CI и раздаётся одинаково и прод-, и dev-стендом, так что зашитое в бандл значение не различалось бы между ними. Вместо VITE_BOT_USERNAME Auth.tsx/Profile.tsx берут его рантаймом через GET /api/v1/config (api.ts::getPublicConfig), который читает BOT_USERNAME из окружения сервера заново на каждый запрос.