Files
netrunner-backend/README.md
T
nineap 74aab70a29
Build & Push Image / Build and push to Gitea Registry (push) Successful in 28s
Build & Push Image / Build and push Caddy (Cloudflare DNS) image (push) Successful in 24s
CI / fmt + clippy + test (push) Failing after 5s
Docs: команда создания первого Owner-аккаунта админки
Флаг --create-owner уже существовал, но нигде не было записано, как
запускать его на проде (через docker compose run, не bare-бинарник) —
пришлось вспоминать вручную.
2026-07-11 03:22:26 +07:00

114 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Netrunner Control Plane
Бэкенд VPN-сервиса «Netrunner»: авторизация (Telegram / анонимный «Ghost Protocol»),
биллинг подписок через TON, реферальная программа, оркестрация VPN-нод по SSH,
Telegram-бот как второй канал входа и встроенная раздача SPA-фронтенда.
Стек: Rust (axum, sqlx/Postgres, teloxide, tokio) + React/TypeScript (Vite) фронтенд,
собранные в единый Docker-образ.
## Структура репозитория
| Путь | Что там | Подробнее |
|---|---|---|
| [`src/`](src) | Rust-бэкенд (control plane) | см. ниже |
| [`src/modules/`](src/modules/README.md) | Бизнес-модули (auth, billing, nodes, referrals, users) | [README](src/modules/README.md) |
| [`src/db/`](src/db/README.md) | Модели и репозиторий (доступ к Postgres) | [README](src/db/README.md) |
| [`frontend/`](frontend/README.md) | React/Vite SPA (личный кабинет, игра, админка нод) | [README](frontend/README.md) |
| [`migrations/`](migrations) | SQL-миграции (sqlx) | — |
| [`templates/init_node.sh`](templates/init_node.sh) | Bash-скрипт инициализации новой VPN-ноды, исполняется по SSH | — |
| [`tests/`](tests) | Интеграционные тесты репозитория (`sqlx::test`, требуют Postgres) | — |
Инфраструктура и процесс деплоя подробно описаны в [`DEPLOYMENT.md`](DEPLOYMENT.md);
список закрытых и остаточных угроз безопасности — в [`SECURITY_AUDIT.md`](SECURITY_AUDIT.md).
## Архитектура бэкенда
Слоями, сверху вниз:
1. **`main.rs`** — точка входа: поднимает пул Postgres, собирает `ApiState`
(репозиторий + сервисы всех модулей) и параллельно запускает HTTP API, Telegram-бота
и фоновые задачи биллинга до общего сигнала остановки (Ctrl+C / SIGTERM).
2. **`api.rs`** — сборка axum-роутера: монтирует контроллеры модулей, rate-limiting
(`tower_governor`), CORS, Prometheus-метрики (`/metrics`), health-пробы (`/health`,
`/health/ready`) и раздачу статики фронтенда как SPA-фолбэк.
3. **`modules/*/controller.rs`** — HTTP-контракты (axum-роуты, JSON DTO), тонкий слой
без бизнес-логики.
4. **`modules/*/service.rs`** — бизнес-логика модуля.
5. **`db::repository::AppRepository`** — трейт доступа к БД; единственная реализация —
`PgRepository` (Postgres/sqlx). Сервисы знают только о трейте, поэтому теоретически
подменяемы в тестах без реальной БД.
6. **`bot.rs`** — Telegram-бот (teloxide), использует те же сервисы, что и HTTP API;
кнопки «Личный Кабинет»/«Тарифы»/«Синдикат» ведут в ЛК через общий WebApp-мост
(короткоживущий bootstrap-токен в URL, фронт меняет его на cookie-сессию через
`POST /auth/exchange`).
7. **`tasks.rs`** — два фоновых цикла: сброс просроченных подписок + сверка блокчейна
на предмет потерянных платежей, и health-check VPN-нод (TCP-пинг раз в 60с,
переоценка `online`/`offline`).
8. **`error.rs`** — единый `AppError` с `IntoResponse`: маппинг в HTTP-статус,
безопасное сообщение наружу и подробности только в логи/метрики.
## Авторизация: единая cookie-сессия для лендинга и ЛК
Один способ входа (Telegram Login Widget / анонимный Ghost Protocol / bootstrap-мост
из бота) выдаёт одну и ту же пару cookie: короткий access-JWT (`nrxp_token`, 15 мин) и
ротируемый непрозрачный refresh-токен (`nrxp_refresh`, 30 дней, хеш хранится в
`refresh_sessions`, путь строго `/api/v1/auth/refresh`). Обе — `HttpOnly; Secure;
SameSite=Lax`; в проде дополнительно `Domain=.netrunner-vpn.com`, поэтому одна и та же
сессия работает и на лендинге (`netrunner-vpn.com`), и на ЛК (`account.netrunner-vpn.com`)
без единого байта в `localStorage` или JS-читаемых cookie. Подробности — в
[`src/modules/auth/README.md`](src/modules/auth/README.md).
## Быстрый старт (локальная разработка)
```bash
cp .env.example .env # заполнить секреты (JWT_SECRET, ARGON_SALT, TELOXIDE_TOKEN, ...)
make dev # docker compose: db + redis + app с hot-reload (cargo watch)
```
Приложение поднимется на `http://localhost:8080` (`/health`, `/health/ready`).
Остальные команды — `make help`.
## Создание первого Owner-аккаунта админки
Единственный способ попасть в `/admin/login` (React-админка) — учётка с ролью
`owner`, логин+пароль (не Telegram). Создаётся one-shot CLI-флагом
`--create-owner` (нужен только `DATABASE_URL`, ни ключей, ни токенов;
пароль вводится скрыто в консоли, не через аргумент/env — не оседает в
истории шелла):
```bash
# Локально/дев (бинарник собран, .env заполнен):
./netrunner-control-plane --create-owner --username admin
# На проде (задеплоено через docker-compose.prod.yml) — выполнить на самом
# VPS из /root/netrunner-core:
docker compose -f docker-compose.prod.yml run --rm app \
./netrunner-control-plane --create-owner --username admin
```
`docker compose run` (не `up`) — контейнер разовый, не публикует порт 8080,
не конфликтует с уже работающим `app`. Логин уже занят — упадёт с понятной
ошибкой, а не тихо перезапишет существующего владельца.
## Nodes, Cyberhack, биллинг — текущее состояние
- **`GET /nodes/routing`** отдаёт полный `TunnelConfig`: `tunnel_*`-поля,
`public_key`/`sni_domain` ноды и одноразовый (60с) session-токен, который
выдаётся только при активной подписке юзера.
- **Health-check VPN-нод** — фоновая задача (`src/tasks.rs`, TCP-пинг раз в 60с)
переоценивает `online`/`offline` сама; ручной `POST /admin/nodes/{id}/restart`
перезапускает прокси-контейнер на ноде по SSH-ключу.
- **Провижининг ноды асинхронный:** `POST /admin/nodes` сразу отвечает `202
Accepted` со статусом `provisioning`, SSH-деплой идёт в фоне.
- **Курс TON кешируется в Redis** (`ton_rate:<currency>`, TTL 60с) —
`BillingService::initiate_payment` бьёт TonAPI только на промах кеша.
- **Cyberhack — server-authoritative.** Доску генерирует и хранит сервер
(`src/modules/users/cyberhack.rs`, порт алгоритма из `Cyberhack/src/lib.rs`);
клиент присылает не итоговый счёт, а сырой путь кликов, который сервер сам
реплеит и валидирует (`POST /users/hack/start` → `POST /users/hack/result`).
- **Telegram-бот:** «Тарифы» и «Синдикат» ведут в тот же ЛК, что и «Личный
Кабинет» — единый WebApp-мост (см. выше).
- **`AdminDashboard.tsx`:** кнопки «Force Restart» и удаления ноды подключены
к соответствующим ручкам бэкенда.