Files
netrunner-backend/src/modules/README.md
T
nineap 52531dd41a Unify auth into refresh-token cookie sessions; server-authoritative nodes and Cyberhack
Auth was effectively broken for every login path: the Telegram handler
built a Set-Cookie header but never attached it to the response, and
Ghost Protocol (seed) login never set a cookie at all — so billing, the
game, and admin actions never actually worked once the frontend was fixed
to stop trying to read an HttpOnly cookie from JS. Replaced the bare
10-minute JWT with AuthService::issue_session: a 15-minute access cookie
plus a rotating 30-day refresh cookie (opaque token, only its SHA-256
hash stored in the new refresh_sessions table); reusing an already-
rotated refresh token now revokes every session for that user. CSRF/CORS
origin allowlists moved from a single hardcoded domain to env-configured
lists so the same session works across the landing and account subdomains.
The bot's WebApp deep links (Личный Кабинет/Тарифы/Синдикат) now bridge
through a short-lived bootstrap token exchanged via POST /auth/exchange
instead of a bare access token in the URL.

Nodes: /nodes/routing now serializes the tunnel_* fields, public_key,
sni_domain and a one-time session token gated on an active subscription
instead of a stub ip/port/protocol list; node provisioning returns 202
immediately and finishes in the background instead of blocking the
request for the whole SSH run; a new background task TCP-pings nodes
every 60s and flips online/offline itself; admin can now force-restart a
node over SSH.

Billing: TON exchange rate is cached in Redis (60s) instead of hitting
TonAPI on every invoice, and the request/response now actually uses the
requested currency and TonAPI's real (uppercase) key casing — previously
only USD/RUB were ever requested and the lookup used the wrong case, so
non-USD/RUB plans could never price correctly.

Cyberhack: the server now generates and stores the board itself and
replays the client's raw click path to compute the score, instead of
clamping a client-reported number — closes the "final score is whatever
the browser sends" hole flagged in the security audit.

Frontend: api.ts no longer tries to read the HttpOnly session cookie from
JS (that never worked) and instead relies on same-origin credentials plus
a silent refresh-and-retry on 401; AdminDashboard's restart/delete actions
are wired up; Auth.tsx drops the artificial delays and requires an
explicit seed download/confirmation before continuing, since a lost Ghost
seed is unrecoverable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 15:29:51 +07:00

3.5 KiB
Raw Blame History

modules/ — бизнес-модули

Каждый модуль — вертикальный срез фичи, оформленный по одному шаблону:

modules/<name>/
  mod.rs         # реэкспорт controller/service
  controller.rs  # axum-роуты: парсинг запроса, вызов service, сериализация ответа
  service.rs     # бизнес-логика; принимает/возвращает доменные типы, не знает про HTTP

controller.rs намеренно тонкий — там нет ветвлений бизнес-правил, только HTTP-контракт (DTO, статус-коды через AppError, middleware-гварды). Вся логика и все инварианты (гонки, лимиты, права) — в service.rs, который работает поверх db::repository::AppRepository и ничего не знает об axum.

Все сервисы Clone (обычно дешёвый клон Arc<dyn AppRepository>) и живут внутри ApiState, который передаётся в роутер и в Telegram-бота — то есть один и тот же BillingService/AuthService/UserService обслуживает и веб, и бота.

Модули

Модуль Ответственность README
auth Вход через Telegram Login Widget, анонимный Ghost Protocol (seed) и bootstrap-мост из бота; единая cookie-сессия (access + ротируемый refresh), guards подробнее
billing Тарифы, инвойсы, оплата через TON, триал, реферальный revshare подробнее
nodes Control Plane VPN-нод: провижининг по SSH, публичные ручки для клиента/лендинга подробнее
referrals Статистика по рефералам и применение промокодов подробнее
users Профиль (агрегированный) и мини-игра Cyberhack подробнее

Сквозные зависимости между модулями

  • auth::process_login при регистрации нового юзера дёргает referrals::create_referral напрямую через репозиторий (без сервиса) — реферальная связь создаётся сразу при первом входе по ref_code.
  • billing::confirm_payment после успешной оплаты сам начисляет реферальный бонус (repo.reward_referrer) — то есть логика вознаграждения за реферала физически лежит в billing, а не в referrals.
  • nodes::controller::get_node_routing читает billing_service.get_subscription из ApiState, чтобы не выдавать одноразовый session-токен ноды юзерам без активной подписки — единственная точка, где nodes смотрит на состояние другого модуля (через уже собранный стейт, не импортируя billing:: напрямую).