Enhance user management and node health check features
- Updated `ListUsersQueryHandler` to include plan names and config quotas in `UserSummaryDto`, enriching user data retrieval. - Implemented `WithPlanNamesAsync` method to fetch plan names based on user plan IDs, improving user experience in the admin interface. - Enhanced `Node` class with a `ConsecutiveProbeFailures` property for better status management during health checks. - Modified `NodeHealthCheckService` to utilize the new `RecordProbe` method, implementing a hysteresis mechanism for node status changes. - Updated frontend components to display user config quotas and plan names, improving clarity in user management. - Enhanced tests for user listing and node status handling to ensure robust functionality and coverage. - Updated documentation to reflect changes in user and node management features.
This commit is contained in:
@@ -382,6 +382,13 @@ approve/reject над `ActivationRequest`.
|
||||
отличие от `VpnConfigDto`): `{ id, userId, userName, label, clientEmail, protocol, location, nodeName,
|
||||
usedUpBytes, usedDownBytes, expiresAt, status, createdAt }`. `search` матчится по `clientEmail`/`label`.
|
||||
|
||||
`UserSummaryDto`: `{ id, userName, role, isActivated, isBlocked, activatedAt, billingEnabled,
|
||||
billingPaidUntil, configQuota, planId, billingPendingReview, planName }`. `configQuota` — фактическая
|
||||
квота конфигов (`-1` = без лимита), `planName` — имя каталожного тарифа по `planId` (`null`, если
|
||||
квота задана вручную); и `planName`, и `billingPendingReview` домешивает `ListUsersQueryHandler` —
|
||||
`IIdentityService` не знает ни про `Plans`, ни про `PaymentRequests`. Менять квоту админ может через
|
||||
`PATCH /api/admin/users/{id}/plan` (см. Admin — Roles & Plans), доплата при этом не создаётся.
|
||||
|
||||
**Блокировка/разблокировка — два отдельных эндпоинта без тела**, не один переключатель `isBlocked`.
|
||||
`StatsDto`: `{ totalUsers, activatedUsers, pendingActivationRequests, totalNodes, onlineNodes,
|
||||
totalConfigs, activeConfigs, totalUsedUpBytes, totalUsedDownBytes }` — считается на лету при запросе,
|
||||
|
||||
@@ -227,9 +227,14 @@ POST /api/configs
|
||||
клиентам через `IXuiPanelGateway.GetClientTrafficAsync`, пишет `VpnConfig.UpdateTraffic(...)` и
|
||||
`TrafficSample`, шлёт `configTrafficUpdated`. Трафик используется только для отображения — лимиты
|
||||
и автоотключение по превышению не реализованы (см. [domain-model.md](domain-model.md)).
|
||||
- **NodeHealthCheckService** — health-probe нод (`IXuiPanelGateway.ProbeAsync`), обновляет `NodeStatus`,
|
||||
шлёт `nodeStatusChanged` группе `admins`. Дополнительно, если у ноды `NotifyOnStatusChange = true`,
|
||||
при каждом переходе Online↔Offline шлёт админам ещё и Telegram-уведомление
|
||||
- **NodeHealthCheckService** — health-probe нод раз в 2 минуты (`IXuiPanelGateway.ProbeAsync`), обновляет
|
||||
`NodeStatus` через `Node.RecordProbe` с **гистерезисом**: Offline выставляется только после 2 подряд
|
||||
неудачных проб (счётчик `Node.ConsecutiveProbeFailures`, обнуляется первой удачной пробой), обратно в
|
||||
Online — по первой же удачной. Так единичные HTTP-заминки панели (таймаут/реавторизация/`No route to
|
||||
host` при моргании сети, ICMP при этом в норме) не порождают ложных переходов и спама уведомлениями.
|
||||
При смене статуса шлёт `nodeStatusChanged` группе `admins`; причина падения и счётчик неудач
|
||||
логируются. Дополнительно, если у ноды `NotifyOnStatusChange = true`, при каждом переходе
|
||||
Online↔Offline шлёт админам ещё и Telegram-уведомление
|
||||
(`ITelegramNotifier.NotifyAdminsNodeStatusChangedAsync`) — опция включается индивидуально на ноду
|
||||
(`PUT /api/admin/nodes/{id}`), по умолчанию выключена.
|
||||
- **TrafficRetentionService** — чистит `TrafficSample` старше N дней (TTL-ретеншн истории трафика).
|
||||
|
||||
@@ -50,6 +50,7 @@ AppUser
|
||||
| `Credentials` | `NodeCredentials` (VO) | Логин + **зашифрованный** пароль (`ISecretProtector`) |
|
||||
| `Location` | `string?` | Страна/город/тег для выбора пользователем |
|
||||
| `Status` | `NodeStatus` | `Online` / `Offline` / `Unknown` |
|
||||
| `ConsecutiveProbeFailures` | `int` | Счётчик подряд неудачных проб для гистерезиса статуса (`RecordProbe`); обнуляется первой удачной пробой |
|
||||
| `IsEnabled` | `bool` | Выключена админом → скрыта из самообслуживания |
|
||||
| `NotifyOnStatusChange` | `bool` | Слать админам в Telegram при каждом переходе Online↔Offline (см. NodeHealthCheckService); по умолчанию `false` |
|
||||
| `LastSyncAt` | `DateTimeOffset?` | Последняя успешная синхронизация |
|
||||
@@ -111,8 +112,9 @@ AppUser
|
||||
не показывают вовсе, а админский список подставляет "?" вместо локации отсутствующего инбаунда.
|
||||
|
||||
> `Node.Status` (health-check раз в 2 минуты, см. `NodeHealthCheckService`) — это диагностический
|
||||
> индикатор для админа, не гейт для создания конфига: он кэшированный и может ложно показывать
|
||||
> `Offline` из-за временного сбоя пробника. Реальную недоступность ноды ловит вызов
|
||||
> индикатор для админа, не гейт для создания конфига: он кэшированный и, несмотря на гистерезис
|
||||
> (`RecordProbe`: Offline лишь после 2 подряд неудачных проб), может отставать от реальности. Реальную
|
||||
> недоступность ноды ловит вызов
|
||||
> `IXuiPanelGateway.AddClientAsync` в момент создания — с честной ошибкой и компенсацией
|
||||
> зарезервированной квоты, а не заранее закэшированным статусом.
|
||||
|
||||
|
||||
@@ -111,6 +111,11 @@ frontend/
|
||||
- **Админка** (`/admin/*`): вкладки — обзор (карточки статистики, без графиков), запросы активации,
|
||||
пользователи, роли, ноды (+ публикация инбаундов), приложения, аудит. Таблицы — обычные `<table>`,
|
||||
без TanStack Table. Блокировка пользователя — с подтверждением.
|
||||
- **Пользователи** (`/admin/users`): колонка «Конфигов» показывает квоту (`configQuota`, `∞` для
|
||||
безлимита) и рядом имя тарифа либо «своя квота». В `UserManageDialog` — блок квоты: выбор
|
||||
каталожного тарифа (применяется сразу, как смена роли) либо своё число конфигов через
|
||||
`PATCH /api/admin/users/{id}/plan`. Подпись явно говорит, что админская смена идёт **без доплаты**
|
||||
и что при понижении уже созданные конфиги не отзываются.
|
||||
- **Состояния**: `isLoading`/`isError`/пусто различаются явно везде (ошибка сети не выглядит как
|
||||
«пусто» — паттерн закреплён после находки в `ActivationGate`, распространён на все admin-списки).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user