379f2a26cf
init_node.sh раньше запускал образ вообще без CLI-аргументов — все провижинингованные ноды молча использовали дефолты и не требовали авторизации, несмотря на то что admin-панель как будто настраивала per-node SNI/порт. Заодно убирает захардкоженный IP control plane. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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::напрямую).