Add inbound country flags and node load in location picker
- Inbound.CountryCode (ISO alpha-2), set on publish; flag shown before the location name (SVG via country-flag-icons on the site, emoji in bot) - Node.Capacity (optional, not a limit); available inbounds report load percent of capacity or active config count, sorted least loaded first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d7743977a1
commit
bb5e59904f
51 files changed
+3160
-284
No files matched your search
+3
-3
@@ -63,7 +63,7 @@ rate-limit'ом (`RateLimiting:AuthPermitLimit`, по умолчанию 20 за
|
||||
|
||||
| Метод | Путь | Тело запроса | Тело ответа |
|
||||
| ------ | --------------------------------- | ------------------------------------ | --------------------------------------- |
|
||||
| GET | `/api/inbounds/available` | — | `AvailableInboundDto[]` |
|
||||
| GET | `/api/inbounds/available` | — | `AvailableInboundDto[]` (`countryCode`, `loadPercent`/`nodeConfigCount`; от менее загруженных нод) |
|
||||
| GET | `/api/configs` | — | `{ configs: VpnConfigDto[], configQuota, planId }` — **без пагинации**, весь список сразу |
|
||||
| POST | `/api/configs` | `{ inboundId, label? }` | `VpnConfigDto` (`200 OK`, не 201) |
|
||||
| PATCH | `/api/configs/{id}` | `{ label? }` | `VpnConfigDto` |
|
||||
@@ -343,7 +343,7 @@ approve/reject над `ActivationRequest`.
|
||||
| ------ | ----------------------------- | ----- | ---------------------------------------------------------------------------- | ------------- |
|
||||
| GET | `/api/admin/nodes` | admin | — | `NodeDto[]` |
|
||||
| POST | `/api/admin/nodes` | admin | `{ name, baseAddress, username, password, location? }` | `NodeDto` |
|
||||
| PUT | `/api/admin/nodes/{id}` | admin | `{ name, baseAddress, location?, isEnabled, notifyOnStatusChange, username?, password? }` | `NodeDto` |
|
||||
| PUT | `/api/admin/nodes/{id}` | admin | `{ name, baseAddress, location?, isEnabled, notifyOnStatusChange, username?, password?, capacity? }` | `NodeDto` |
|
||||
| DELETE | `/api/admin/nodes/{id}` | admin | — | `204 No Content` |
|
||||
| POST | `/api/admin/nodes/{id}/sync` | admin | — | `{ inboundsSynced, status }` |
|
||||
| POST | `/api/admin/nodes/{id}/probe` | admin | — | `{ isReachable, errorMessage, status }` |
|
||||
@@ -360,7 +360,7 @@ approve/reject над `ActivationRequest`.
|
||||
| Метод | Путь | Роль | Тело запроса | Тело ответа |
|
||||
| ----- | -------------------------------------- | ----- | ---------------------------------------------------------------------------- | ------------- |
|
||||
| GET | `/api/admin/inbounds` | admin | query: `nodeId?` | `InboundDto[]` |
|
||||
| PUT | `/api/admin/inbounds/{id}/publish` | admin | `{ isPublished, displayName?, allowedRoleIds? }` | `InboundDto` |
|
||||
| PUT | `/api/admin/inbounds/{id}/publish` | admin | `{ isPublished, displayName?, countryCode?, allowedRoleIds? }` | `InboundDto` |
|
||||
| DELETE | `/api/admin/inbounds/{id}` | admin | только для `IsAvailable=false`, иначе `Inbounds.StillAvailable` | `204` |
|
||||
|
||||
## Admin — Users & Stats
|
||||
|
||||
@@ -52,6 +52,7 @@ AppUser
|
||||
| `Status` | `NodeStatus` | `Online` / `Offline` / `Unknown` |
|
||||
| `ConsecutiveProbeFailures` | `int` | Счётчик подряд неудачных проб для гистерезиса статуса (`RecordProbe`); обнуляется первой удачной пробой |
|
||||
| `IsEnabled` | `bool` | Выключена админом → скрыта из самообслуживания |
|
||||
| `Capacity` | `int?` | Ёмкость — сколько активных конфигов нода нормально тянет. Не лимит: знаменатель загрузки (%), которую пользователь видит при выборе локации; `null` — показывается число конфигов |
|
||||
| `NotifyOnStatusChange` | `bool` | Слать админам в Telegram при каждом переходе Online↔Offline (см. NodeHealthCheckService); по умолчанию `false` |
|
||||
| `LastSyncAt` | `DateTimeOffset?` | Последняя успешная синхронизация |
|
||||
| `CreatedAt` | `DateTimeOffset`| |
|
||||
@@ -82,6 +83,7 @@ AppUser
|
||||
| `IsAvailable` | `bool` | Существует ли инбаунд на панели по последней синхронизации (см. ниже) |
|
||||
| `AllowedRoleIds` | `Guid[]` | Id ролей, которым разрешено создавать конфиги (native PostgreSQL `uuid[]`; не навигация на `AppRole` — тот в Infrastructure/Identity, Domain на него не ссылается) |
|
||||
| `DisplayName` | `string?` | Витринное имя для пользователя, напр. «Германия (Trojan)» |
|
||||
| `CountryCode` | `string?` | ISO 3166-1 alpha-2 (`DE`) — флаг перед именем у пользователя; задаётся при публикации, только отображение |
|
||||
| `LastSyncAt` | `DateTimeOffset?` | |
|
||||
|
||||
Инварианты: конфиг можно создать только если `IsPublished && Node.IsEnabled`, и **роль пользователя
|
||||
@@ -118,7 +120,7 @@ AppUser
|
||||
> `IXuiPanelGateway.AddClientAsync` в момент создания — с честной ошибкой и компенсацией
|
||||
> зарезервированной квоты, а не заранее закэшированным статусом.
|
||||
|
||||
> **Пользователю показываем только `DisplayName` + протокол.** Адрес/хост ноды, `RemoteInboundId`,
|
||||
> **Пользователю показываем только `DisplayName` + протокол (+ флаг по `CountryCode`).** Адрес/хост ноды, `RemoteInboundId`,
|
||||
> `Port` и прочие детали 3x-ui в пользовательские DTO не попадают (только в админские).
|
||||
|
||||
### VpnConfig — конфиг пользователя (клиент в 3x-ui)
|
||||
|
||||
+5
-1
@@ -89,7 +89,11 @@ frontend/
|
||||
трафик, статус), кнопки на карточке — показать ссылку/QR (запрашивает `GET .../link` по клику,
|
||||
не сразу при создании), перевыпустить, отозвать; отдельная карточка «Общая подписка». Для
|
||||
неактивированного — `ActivationGate` вместо дашборда.
|
||||
- **Создание конфига**: диалог — выбор инбаунда (по `displayName`) + метка. Лимит одновременных IP
|
||||
- **Создание конфига**: диалог — выбор инбаунда (по `displayName`, с флагом страны) + метка. Флаги —
|
||||
SVG из `country-flag-icons` (курируемый список в `shared/lib/countries.ts`; emoji-флаги Windows не рисует),
|
||||
в Telegram-боте — emoji. У каждой локации — загрузка её ноды: «светофор» + % от `Node.Capacity`
|
||||
(<50 / <80 / ≥80), либо число конфигов на сервере, если ёмкость не задана; порядок — с сервера,
|
||||
от менее загруженных. Лимит одновременных IP
|
||||
(`limitIp` в 3x-ui) выставляется автоматически по квоте роли пользователя (`AppRole.MaxIpLimit`) —
|
||||
в форме создания не настраивается. После успеха карточка конфига появляется в списке; ссылку/QR
|
||||
пользователь открывает отдельно.
|
||||
|
||||
Reference in new issue
Block a user