Commit Graph

14 Commits

Author SHA1 Message Date
nineap 403a6127b1 Переименовать GITEA_REGISTRY_TOKEN → GT_REGISTRY_TOKEN (правила именования Gitea не пускают префикс GITEA_)
Build & Push Image / Build and push to Gitea Registry (push) Failing after 10m34s
Build & Push Image / Build and push Caddy (Cloudflare DNS) image (push) Failing after 4m6s
CI / fmt + clippy + test (push) Failing after 21m30s
2026-07-10 21:28:25 +07:00
nineap c617c05233 Переезд с ghcr.io на встроенный Container Registry Gitea
Build & Push Image / Build and push to Gitea Registry (push) Failing after 14s
Build & Push Image / Build and push Caddy (Cloudflare DNS) image (push) Failing after 13s
CI / fmt + clippy + test (push) Has been cancelled
CI (build/deploy) и рантайм самого бэкенда (провижининг новых нод через
NodeService::provision_node_via_ssh — раньше читал GHCR_TOKEN, теперь
GITEA_REGISTRY_TOKEN) теперь используют gitea.netrunner-vpn.com вместо
ghcr.io. .github/workflows остались нетронутыми — та копия для настоящего
GitHub CI, там ghcr.io уместен и дальше (свой GITHUB_TOKEN, не наш секрет).
2026-07-10 20:44:03 +07:00
nineap 864d4bc852 CI сам собирает .env на прод/dev VPS из repo Variables/Secrets
Раньше .env копировался на сервер руками по .env.example — теперь
deploy-prod/deploy-dev в deploy.yml пишут его сами перед docker compose up,
тем же принципом, что уже был у netrunner-data. .env.example остаётся
только как справочник по составу переменных, не инструкция "скопируй на
сервер".
2026-07-10 18:18:47 +07:00
nineap 91e214dad2 Единый мониторинг: бизнес-метрики + вынос Prometheus/Loki/Grafana на VPS с данными
Prometheus/Loki/Grafana больше не живут внутри backend-стека — единый
стек теперь на VPS с данными (netrunner-data), т.к. ему нужно видеть
ещё лендинг и все прокси-ноды, не только бэкенд. Отсюда только тонкий
promtail, пушащий логи по сети (LOKI_PUSH_URL).

Новые бизнес-метрики поверх уже существующих generic HTTP-метрик:
payments_total{provider,status}, nodes_online/nodes_total,
support_tickets_total{status}, referral_signups_total,
partner_commission_total{currency} — тем же макросом metrics::counter!/
gauge!, что уже был в api.rs/error.rs.

Новый гейт-эндпоинт GET /api/v1/admin/observability/check (только
Owner/Moderator) — для Caddy forward_auth перед Grafana (см.
netrunner-data/observability/Caddyfile), встройка в админку через
GRAFANA_PUBLIC_URL в GET /api/v1/config.

templates/init_node.sh — добавляет --metrics-port 9093 в провижининг
новых прокси-нод.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 23:20:44 +07:00
nineap 3feb2f431c Убирает свой Postgres — БД физически переезжает на VPS с Vaultwarden
backend будет жить на отдельном, ещё не арендованном VPS — свой
контейнер `db` и бэкапы (scripts/backup.sh) здесь больше не нужны и
переехали в netrunner-data, где Postgres теперь физически стоит.
DATABASE_URL собирается из DB_HOST/DB_PORT (внешний IP того VPS) с
обязательным sslmode=require — трафик пересекает публичную сеть между
двумя разными серверами.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 17:17:20 +07:00
nineap 591643d364 Чинит .env.example: убирает мёртвый ADMIN_TG_ID, добавляет PROXY_INTERNAL_SECRET
ADMIN_TG_ID нигде не читается с тех пор, как admin_guard заменили на ролевую
систему (owner/staff/node_manage/partner_guard) — оставлять его в примерах
вводит в заблуждение. PROXY_INTERNAL_SECRET, наоборот, обязателен (main.rs
падает без него при старте), но отсутствовал во всех трёх .env*.example.

Заодно поправлен src/modules/auth/README.md — описывал admin_guard,
которого больше не существует.
2026-07-09 15:33:33 +07:00
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
nineap acfcdbe90e Make the Secure cookie attribute configurable for TLS-less dev stands
Session cookies were hardcoded Secure, which is correct for prod but
silently breaks every non-localhost HTTP dev deployment: browsers (and
any spec-compliant HTTP client, verified with curl against a container IP
and against dev.netrunner-vpn.com directly) refuse to store a Secure
cookie received over plain HTTP unless the host is localhost. The dev VPS
serves everything over plain HTTP with no reverse-proxy/TLS by design, so
every login there was silently failing to persist a session at all —
this is a bigger, more fundamental gap than the cookie-sharing-with-the-
landing question that led to finding it.

Added COOKIE_SECURE (defaults true) alongside the existing COOKIE_DOMAIN,
logs a loud warning when disabled, and turned it off for the dev-remote
and local docker-dev env templates. Verified end-to-end against a
non-localhost address that the cookie now actually gets stored and a
follow-up authenticated request succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 19:20:14 +07:00
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
Kirill f06cc48763 security: fix CSRF/ownership/timing vulns; add billing reconciliation
- auth/guard: добавить CSRF-проверку Origin/Referer для cookie-мутаций,
  исправить инвертированную логику admin_guard (пускал всех, блокировал админов),
  перевести admin_guard на from_fn_with_state
- auth/service: timing-safe сравнение HMAC через verify_slice вместо != по hex;
  криптостойкий Seed через Alphanumeric (был цифровой 0-9)
- billing: confirm_payment проверяет owner (caller_id == invoice.user_id);
  активация триала только для Telegram-аккаунтов (Sybil-защита);
  реализована reconcile_pending_payments — сверка blockchain->pending-инвойсы
- billing/providers: get_recent_transactions перенесён в трейт PaymentProvider
- nodes/service: unwrap() -> map_err по всему SSH-пути; убран TcpStream import
- docker: multi-stage build с cargo-chef, non-root user, healthcheck;
  prod-compose добавляет one-shot migrate-сервис, убирает проброс порта БД
- deploy.yml: параметр image_tag, set -e, pull всего стека вместо только app
- users/service: обработка NO_TICKETS / NO_USER из БД вместо catch-all

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 17:58:24 +07:00
nineap 26d6f2db8a deploy change, update token system, fix abuses and add admin verify 2026-05-26 12:50:25 +07:00
nineap 1ef93c2025 [AI] fix some modules, develop the big part of modules. TODO fix critical errors, update pipeline, test everything 2026-05-16 19:16:50 +07:00
nineap 693e48af59 backend sliced by modules 2026-05-02 18:24:57 +07:00
nineap 4057d039b8 pipeline and codebase updates 2026-04-22 10:16:58 +07:00