Refactor environment configuration and update documentation for MVP status
CI / Backend (build + test) (push) Successful in 1m15s
CI / Frontend (lint + typecheck + build) (push) Successful in 30s

- Removed deprecated Telegram user ID configuration from `.env.example` and added a new setting for admin Telegram user IDs.
- Updated `CLAUDE.md` to reflect the current MVP status, detailing completed features and testing coverage.
- Enhanced `README.md` with quick start instructions for Docker setup and clarified project status.
- Revised API design documentation to include updated error handling and request/response structures.
- Improved frontend documentation to outline the project structure and technologies used.
This commit is contained in:
Leonid Pershin
2026-07-02 14:12:50 +03:00
parent 7e8435ee76
commit cdd67f8e2b
14 changed files with 896 additions and 616 deletions
+67 -44
View File
@@ -1,75 +1,98 @@
# Roadmap
Порядок реализации по этапам (milestones). Каждый этап — работоспособный инкремент.
**MVP полностью реализован** — все этапы M0–M8 закрыты. Ниже — ретроспектива по этапам (как было
задумано → что реально сделано, с честными пометками о расхождениях) и раздел [Backlog](#backlog-после-mvp)
с тем, что осталось за рамками MVP осознанно.
## M0 — Каркас и инфраструктура
- Solution + 4 проекта (Domain/Application/Infrastructure/Api), ссылки по Clean Architecture.
- `Directory.Build.props`, `.editorconfig`, nullable + анализаторы, `dotnet format` в CI.
- EF Core + Npgsql, первая миграция.
- Scaffolding фронта: Vite + React + TS + Tailwind + shadcn/ui + TanStack Query/Router; **тема light/dark/system** (провайдер + переключатель); i18n (RU/EN); dev-прокси `/api`,`/hubs` на бэк.
## 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-compose `app` + `db` (PostgreSQL); `ForwardedHeaders`
(TLS — внешним прокси); авто-применение миграций на старте.
- Health-check `/health`, Serilog, OpenAPI + Scalar.
- 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 рядом с postgres, отдаёт SPA и `/health`,
есть миграции, CI зелёный. ✅ Достигнуто — включая полную проверку `docker compose up` end-to-end.
## M1 — Аутентификация и сидинг
## M1 — Аутентификация и сидинг
- ASP.NET Core Identity (`AppUser`/`AppRole` c `MaxConfigs`); `DbInitializer`: системные роли
`admin`/`user` и учётка админа + Telegram id админов из env ([`.env.example`](../.env.example)).
- **Вход по username** (email не используется); JWT access + refresh (httpOnly cookie, ротация, хранение
хэшей), CSRF на refresh, Identity lockout, rate-limit на `/auth/*`; смена пароля.
`admin`/`user` и учётка админа из env ([`.env.example`](../.env.example)).
- **Вход по username** (email не используется); JWT access + refresh (httpOnly cookie, ротация,
`Secure` по факту HTTPS-запроса, хранение хэшей), Identity lockout, rate-limit на `/auth/*`;
смена пароля. Явного анти-CSRF токена нет — обоснование в [architecture.md](architecture.md#безопасность).
- Регистрация: новый пользователь → роль `user`, `IsActivated = false`.
- Фронт: страницы login/register (username), стор авторизации, refresh-flow, guard-маршруты.
- **Готово, когда**: регистрация/вход/refresh/logout по username работают, админ засидан, новый юзер неактивен.
- **Готово, когда**: регистрация/вход/refresh/logout по username работают, админ засидан, новый юзер неактивен. ✅ Достигнуто.
## M2 — Роли и активация
- Домен: динамические роли (CRUD `admin`, квота `MaxConfigs`), `ActivationRequest`.
- Команды/запросы: CreateRole/UpdateRole/DeleteRole, ChangeUserRole (одна роль), RequestActivation (с комментарием),
ApproveActivation/RejectActivation.
- Эндпоинты активации (user + admin) и ролей; policy `RequireActivated`.
## 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`).
- Команды/запросы: RegisterNode, SyncNode, Probe, ListNodes, ListInbounds, PublishInbound (с выбором ролей).
## 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 «Германия (Trojan)» для выбранных ролей.
- **Готово, когда**: админ подключает реальную 3x-ui и публикует inbound для выбранных ролей. ✅ Достигнуто
(удаление ноды с активными конфигами пока не блокируется — известный пробел, см. [tech-stack.md](tech-stack.md)).
## M4 — Конфиги пользователя (ядро продукта)
- Домен `VpnConfig` (создание, отзыв, ротация, статусы; инварианты: активирован + квота роли (грандфазеринг)
+ доступ роли к инбаунду; проверка квоты в транзакции; схема `ClientEmail`).
## M4 — Конфиги пользователя (ядро продукта)
- Домен `VpnConfig` (создание, отзыв, ротация; инварианты: активирован + квота роли (грандфазеринг)
+ доступ роли к инбаунду; квота — под `pg_advisory_xact_lock`; схема `ClientEmail`).
- CreateVpnConfig (с `label`/`deviceLimit``limitIp`), EditVpnConfig, RotateVpnConfig, RevokeVpnConfig,
GetMyConfigs, GetConfigLink, ListAvailableInbounds; connection string + QR.
- Подписка: агрегированная `/sub/{userToken}` (все конфиги) + по конфигу `/sub/{configToken}`;
заголовки `Subscription-Userinfo` / `profile-update-interval`.
- Самоудаление аккаунта (`DELETE /api/auth/me`): отзыв всех конфигов + удаление данных.
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, отзыв, перевыпуск, настройки аккаунта.
- **Готово, когда**: активированный юзер создаёт рабочий конфиг в доступном инбаунде в пределах квоты; работает агрегированная подписка.
- Фронт: дашборд (метки, лимит устройств), создание/редактирование, страница инструкций, ссылка/QR
по кнопке, отзыв, перевыпуск, настройки аккаунта.
- **Готово, когда**: активированный юзер создаёт рабочий конфиг в доступном инбаунде в пределах квоты;
работает подписка. ✅ Достигнуто. Лимиты трафика/срока конфига — не реализованы, backlog
(см. [domain-model.md](domain-model.md)).
## M5 — Синхронизация трафика и realtime
- `TrafficSyncService` (обход нод, обновление трафика/статусов, `TrafficSample`); реконсиляция дрейфа с 3x-ui.
## M5 — Синхронизация трафика и realtime
- `TrafficSyncService` (обход включённых нод, обновление трафика, `TrafficSample`) — только для
отображения, без активной реконсиляции дрейфа (недоступная нода/незнакомый клиент — тихо пропускаются).
- `NodeHealthCheckService`; `TrafficRetentionService` (TTL-чистка истории).
- SignalR `PanelHub` + `IRealtimeNotifier`; события трафика/статусов/нод/активации.
- Фронт: живые прогресс-бары трафика, статусы онлайн, реакция на превышение лимита/срока.
- **Готово, когда**: трафик и статусы обновляются в UI без перезагрузки.
- SignalR `PanelHub` + `IRealtimeNotifier` (реализован в `Api/Hubs/`, не в Infrastructure); события
`configTrafficUpdated`/`configStatusChanged`/`nodeStatusChanged`/`activationRequested`/`userActivated`.
- Фронт: живые обновления трафика/статусов без перезагрузки. Реакции на превышение лимита/срока нет —
таких лимитов не существует (см. M4).
- **Готово, когда**: трафик и статусы обновляются в UI без перезагрузки. ✅ Достигнуто.
## M6 — Админ-статистика, управление пользователями, аудит
- ListUsers, BlockUser (→ отключение конфигов в 3x-ui) / UnblockUser, ChangeUserRole, ResetUserPassword (без привязки TG), GetUserConfigs, force-revoke, GetStats.
- `AuditLog`: запись значимых действий (Web/Telegram/System) + эндпоинт `/api/admin/audit`.
## M6 — Админ-статистика, управление пользователями, аудит
- ListUsers, BlockUser (→ отключение конфигов в 3x-ui) / UnblockUser (два отдельных эндпоинта),
ChangeUserRole, ResetUserPassword, GetUserConfigs, ForceRevokeConfig, GetStats.
- `AuditLog`: запись значимых действий (активация, блокировка, смена роли, ноды/инбаунды — источник
всегда `Web`, т.к. пишется из тех же хендлеров, что вызывает и бот) + эндпоинт `/api/admin/audit`.
- Каталог приложений: админ-CRUD `ClientApp` (`/api/admin/apps`) — название, ссылка, ОС, порядок, вкл/выкл.
- Фронт: таблицы пользователей/конфигов/ролей, журнал аудита, графики трафика (Recharts), сводки.
- **Готово, когда**: админ видит статистику и журнал, управляет пользователями/ролями/конфигами; блокировка гасит VPN.
- Фронт: таблицы пользователей/ролей/нод/приложений (обычные `<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`, меню, «Мои конфиги» (`GetMyConfigsQuery`), `/login`, `/unlink`, `/requests`, `/help`.
- Команды бота: `/start` (+ `link_<token>`/`login_<requestId>` deep-link payload), «Мои конфиги»
(`/configs`, текстовый список, без ссылок/QR), `/unlink`, `/requests`, `/help`.
- **Админ в боте**: уведомления о запросах активации + inline «Активировать/Отклонить», `/requests` (по Telegram id из env).
- **DM-уведомления юзеру**: активация (`ApproveActivationCommandHandler`), блокировка (`BlockUserCommandHandler`), принудительный отзыв конфига админом (`ForceRevokeConfigCommandHandler`) — если Telegram привязан. Бот — read-only по конфигам.
- **Готово, когда**: юзер привязывает Telegram, входит без пароля, видит конфиги; админ активирует запросы прямо в боте. ✅ Достигнуто.