- Added inline button functionality to the `/configs` command, allowing users to request connection strings for their configurations without displaying them in chat history. - Introduced a constant message for unlinked Telegram accounts to improve user understanding of the linking process. - Updated the handling of configuration messages to include inline buttons for better user interaction and experience.
15 KiB
15 KiB
Roadmap
MVP полностью реализован — все этапы M0–M8 закрыты. Ниже — ретроспектива по этапам (как было задумано → что реально сделано, с честными пометками о расхождениях) и раздел Backlog с тем, что осталось за рамками MVP осознанно.
M0 — Каркас и инфраструктура ✅
- Solution (
PnvPanel.slnx) + 4 проекта (Domain/Application/Infrastructure/Api), ссылки по Clean Architecture. Directory.Build.props,.editorconfig, nullable включены.dotnet format— локальная команда, в CI не запускается (CI гоняет только build/test).- EF Core + Npgsql, миграции.
- Scaffolding фронта: Vite + React + TS + Tailwind + shadcn-стиль поверх Radix + TanStack Query/Router;
тема light/dark/system (провайдер + переключатель); i18n (RU/EN); dev-прокси
/api,/hubsна бэк. - Единый контейнер: multi-stage Dockerfile (node → dotnet publish → aspnet), Api раздаёт SPA из
wwwroot(fallback наindex.html); docker-composeapp+db(PostgreSQL);ForwardedHeaders(TLS — внешним прокси); авто-применение миграций на старте. - Health-check
/health, Serilog, нативный OpenAPI (Microsoft.AspNetCore.OpenApi) + Scalar (без Swashbuckle). - CI (GitHub Actions):
dotnet build/test+pnpm build/lint/typecheck(без деплоя). - Готово, когда: единый образ поднимается в docker-compose рядом с postgres, отдаёт SPA и
/health, есть миграции, CI зелёный. ✅ Достигнуто — включая полную проверкуdocker compose upend-to-end.
M1 — Аутентификация и сидинг ✅
- ASP.NET Core Identity (
AppUser/AppRolecMaxConfigs);DbInitializer: системные ролиadmin/userи учётка админа из env (.env.example). - Вход по username (email не используется); JWT access + refresh (httpOnly cookie, ротация,
Secureпо факту HTTPS-запроса, хранение хэшей), Identity lockout, rate-limit на/auth/*; смена пароля. Явного анти-CSRF токена нет — обоснование в architecture.md. - Регистрация: новый пользователь → роль
user,IsActivated = false. - Фронт: страницы login/register (username), стор авторизации, refresh-flow, guard-маршруты.
- Готово, когда: регистрация/вход/refresh/logout по username работают, админ засидан, новый юзер неактивен. ✅ Достигнуто.
M2 — Роли и активация ✅
- Домен: динамические роли (CRUD, квота
MaxConfigs),ActivationRequest. - Команды/запросы: CreateRole/UpdateRole/DeleteRole, ChangeUserRole (одна роль), RequestActivation (с комментарием), ApproveActivation/RejectActivation.
- Эндпоинты активации (user + admin) и ролей; проверка активации/роли — inline в хендлерах
и
RequireAuthorization(...)на эндпоинте, без отдельных именованных policy. - Фронт: экран «запросить активацию» (с комментарием), админ-очередь запросов, управление ролями/назначением.
- Готово, когда: юзер запрашивает активацию с комментарием, админ на сайте активирует; роли с квотами работают. ✅ Достигнуто.
M3 — Ноды и публикация inbounds (по ролям) ✅
- Домен
Node/Inbound(+AllowedRoles,DisplayName); портIXuiPanelGateway+ единственная реализацияXuiPanelGateway(кэш клиента per-node внутри неё,ThreeXui.Net); шифрование секретов нод (ISecretProtector/ASP.NET Data Protection). - Команды/запросы: RegisterNode, UpdateNode, DeleteNode, SyncNode, ProbeNode, ListNodes, ListInbounds, PublishInbound (с выбором ролей).
- Админка нод/инбаундов на фронте (публикация с
displayNameиallowedRoleIds). - Готово, когда: админ подключает реальную 3x-ui и публикует inbound для выбранных ролей. ✅ Достигнуто (удаление ноды с активными конфигами пока не блокируется — известный пробел, см. tech-stack.md).
M4 — Конфиги пользователя (ядро продукта) ✅
- Домен
VpnConfig(создание, отзыв, ротация; инварианты: активирован + квота роли (грандфазеринг)- доступ роли к инбаунду; квота — под
pg_advisory_xact_lock; схемаClientEmail).
- доступ роли к инбаунду; квота — под
- CreateVpnConfig (с
label/deviceLimit→limitIp), EditVpnConfig, RotateVpnConfig, RevokeVpnConfig, GetMyConfigs, GetConfigLink, ListAvailableInbounds; connection string по запросу (не сразу при создании), QR строится на фронте. - Подписка: один эндпоинт
/sub/{token}— токен либо агрегированный (AppUser.SubscriptionToken, все конфиги), либо по одному конфигу (VpnConfig.SubscriptionToken); заголовкиSubscription-Userinfo/Profile-Update-Interval. - Самоудаление аккаунта (
DELETE /api/auth/me): отзыв всех активных конфигов + удалениеAppUser. - Каталог приложений
ClientApp(домен +GET /api/appsпо ОС; сид изseed/client-apps.json) + страница инструкций на фронте. - Фронт: дашборд (метки, лимит устройств), создание/редактирование, страница инструкций, ссылка/QR по кнопке, отзыв, перевыпуск, настройки аккаунта.
- Готово, когда: активированный юзер создаёт рабочий конфиг в доступном инбаунде в пределах квоты; работает подписка. ✅ Достигнуто. Лимиты трафика/срока конфига — не реализованы, backlog (см. domain-model.md).
M5 — Синхронизация трафика и realtime ✅
TrafficSyncService(обход включённых нод, обновление трафика,TrafficSample) — только для отображения, без активной реконсиляции дрейфа (недоступная нода/незнакомый клиент — тихо пропускаются).NodeHealthCheckService;TrafficRetentionService(TTL-чистка истории).- SignalR
PanelHub+IRealtimeNotifier(реализован вApi/Hubs/, не в Infrastructure); событияconfigTrafficUpdated/configStatusChanged/nodeStatusChanged/activationRequested/userActivated. - Фронт: живые обновления трафика/статусов без перезагрузки. Реакции на превышение лимита/срока нет — таких лимитов не существует (см. M4).
- Готово, когда: трафик и статусы обновляются в UI без перезагрузки. ✅ Достигнуто.
M6 — Админ-статистика, управление пользователями, аудит ✅
- ListUsers, BlockUser (→ отключение конфигов в 3x-ui) / UnblockUser (два отдельных эндпоинта), ChangeUserRole, ResetUserPassword, GetUserConfigs, ForceRevokeConfig, GetStats.
AuditLog: запись значимых действий (активация, блокировка, смена роли, ноды/инбаунды — источник всегдаWeb, т.к. пишется из тех же хендлеров, что вызывает и бот) + эндпоинт/api/admin/audit.- Каталог приложений: админ-CRUD
ClientApp(/api/admin/apps) — название, ссылка, ОС, порядок, вкл/выкл. - Фронт: таблицы пользователей/ролей/нод/приложений (обычные
<table>, без TanStack Table), журнал аудита, статистика карточками (без графиков —rechartsустановлен, но не подключён). - Готово, когда: админ видит статистику и журнал, управляет пользователями/ролями/конфигами; блокировка гасит VPN. ✅ Достигнуто.
M7 — Telegram-бот ✅
- Библиотека Telegram.Bot,
TelegramBotHostedService(long polling) в процессе Api,IOptions<TelegramOptions>. - Домен: поля Telegram у
AppUser,TelegramLinkToken,TelegramLoginRequest. - Флоу привязки (
LinkTelegramCommand) + эндпоинтlink-token/unlink. - Passwordless-вход:
login-request+ подтверждение в боте (ApproveTelegramLoginCommand) → выпуск JWT; поллинг завершения на фронте (GET /api/auth/telegram/login-request/{id}). - Команды бота:
/start(+link_<token>/login_<requestId>deep-link payload), «Мои конфиги» (/configs— по сообщению на конфиг, с inline-кнопкой «🔗 Показать ссылку», раскрывающей connection string по запросу через тот жеGetConfigLinkQuery, что и веб; ссылка не выводится сразу в списке, чтобы не светиться в истории чата без явного действия юзера),/unlink,/requests,/help. - Админ в боте: уведомления о запросах активации + inline «Активировать/Отклонить»,
/requests(по Telegram id из env). - DM-уведомления юзеру: активация (
ApproveActivationCommandHandler), блокировка (BlockUserCommandHandler), принудительный отзыв конфига админом (ForceRevokeConfigCommandHandler) — если Telegram привязан. Бот — read-only по конфигам (только просмотр/показ ссылки, без создания/ротации/отзыва). - Готово, когда: юзер привязывает Telegram, входит без пароля, видит конфиги; админ активирует запросы прямо в боте. ✅ Достигнуто.
- Перенесено в backlog (не реализовано в MVP): восстановление пароля через бота (
/resetpasswordс одноразовой ссылкой) — сейчас сброс пароля только через админа (ResetUserPasswordCommand); QR-картинкой в сообщениях бота (пока только текстовая ссылка). Фронтовые кнопки «Войти через Telegram»/«Привязать Telegram» реализованы в отдельной итерации (см. M0 фронт).
M8 — Закалка (hardening) ✅
- Тесты:
PnvPanel.Domain.Tests(54, чистые unit-тесты инвариантов сущностей),PnvPanel.Application.Tests(71, CQRS-хендлеры на EF Core InMemory + NSubstitute-моки портов),PnvPanel.IntegrationTests(Testcontainers.PostgreSql +WebApplicationFactory<Program>— реальный HTTP-контракт, включая проверкуpg_advisory_xact_lockпод параллельной нагрузкой на квоту конфигов). - Rate-limiting, аудит-лог, единообразные
ProblemDetails, ретеншнTrafficSample— сделаны в M5/M6. - CI (
.github/workflows/ci.yml):dotnet build/test(backend, включая интеграционные — наubuntu-latestDocker доступен) +pnpm lint/typecheck/build(frontend), без деплоя. - Прод-
docker-compose.yml:env_file: .envпрокидывает все секреты в контейнерapp, томdp_keysдля key-ring Data Protection (переживает пересоздание контейнера), healthcheckappчерезGET /health(curl добавлен в runtime-образ). TLS — внешним прокси (без изменений). - Готово, когда: зелёный CI, покрытие ключевых сценариев, готовность к деплою. ✅ Достигнуто (интеграционные тесты прогнаны локально через Testcontainers после появления Docker на машине разработки — 134/134 зелёных; там же впервые собран и проверен единый Docker-образ и docker-compose стек end-to-end).
Backlog (после MVP)
- Полное самообслуживание в боте (создание/ротация/отзыв конфигов) — в MVP бот read-only.
- Полная регистрация аккаунта через Telegram (в MVP — только привязка); Telegram Login Widget как альтернатива.
- Тарифы/биллинг/платежи, автопродление, промокоды.
- Реферальная программа; расширенные уведомления (через Telegram/веб — email в проекте не используется).
- Балансировка/выбор оптимальной ноды, автоскейл.
- OpenTelemetry-трейсинг, метрики, дашборды.
- Вынос фоновых задач в Hangfire/Quartz; TimescaleDB для истории трафика.
- Мультиязычность (RU/EN и далее).