API Design
REST поверх HTTP/JSON, авторизация — Authorization: Bearer <access-token> (кроме публичных).
Ошибки — application/problem+json (ProblemDetails). Пагинация — ?page=&pageSize=,
ответ PagedList<T> (items, total, page, pageSize). Все даты — ISO-8601 UTC.
Базовый префикс: /api (без версионирования в MVP — единый фронт+бек; версии введём при
необходимости). Ниже — контракт MVP (может уточняться при реализации).
Auth
| Метод |
Путь |
Роль |
Описание |
| POST |
/api/auth/register |
— |
Регистрация { username, password } |
| POST |
/api/auth/login |
— |
Вход { username, password } → access (body) + refresh (httpOnly cookie) |
| POST |
/api/auth/refresh |
— |
Обновление access по refresh-cookie (ротация) |
| POST |
/api/auth/logout |
user |
Отзыв refresh-токена |
| POST |
/api/auth/change-password |
user |
Смена пароля { currentPassword, newPassword } |
| GET |
/api/auth/me |
user |
Текущий профиль + роль + isActivated + telegramLinked |
| DELETE |
/api/auth/me |
user |
Самоудаление аккаунта (отзыв всех конфигов + удаление данных; аудит анонимизируется) |
Вход по username. Email в системе не используется. Забыт пароль:
при привязанном Telegram — восстановление через бота; иначе — сброс админом (см. Admin).
Auth — Telegram (привязка и passwordless-вход)
| Метод |
Путь |
Роль |
Описание |
| POST |
/api/auth/telegram/link-token |
user |
Создать токен привязки → { deepLink, qr, expiresAt } |
| POST |
/api/auth/telegram/unlink |
user |
Отвязать Telegram от аккаунта |
| POST |
/api/auth/telegram/login-request |
— |
Инициировать вход → { requestId, deepLink, qr, expiresAt } |
| GET |
/api/auth/telegram/login-request/{id} |
— |
Статус запроса; при Approved выдаёт access + refresh-cookie |
GET …/login-request/{id} (поллинг; альтернатива — событие SignalR) → варианты ответа:
Сами апдейты Telegram (/start, кнопки) обрабатывает in-process бот (long polling), а не HTTP-эндпоинты.
Контракты команд бота — в telegram-bot.md.
Configs (пользователь)
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/inbounds/available |
user |
Инбаунды, доступные роли пользователя (для выбора при создании) |
| GET |
/api/configs |
user |
Список своих конфигов (пагинация) |
| POST |
/api/configs |
user |
Создать конфиг { inboundId, label?, deviceLimit? } (проверки: активирован, квота роли, доступ роли к инбаунду) |
| PATCH |
/api/configs/{id} |
user |
Изменить { label?, deviceLimit? } (deviceLimit → limitIp в 3x-ui) |
| GET |
/api/configs/{id} |
user |
Детали конфига (метка, трафик, устройства, статус) |
| GET |
/api/configs/{id}/link |
user |
Connection string + subscriptionUrl + QR-payload |
| POST |
/api/configs/{id}/rotate |
user |
Перевыпустить конфиг (новый UUID/ссылка; квоту не тратит) |
| DELETE |
/api/configs/{id} |
user |
Отозвать конфиг (удаляет клиента в 3x-ui) |
| GET |
/api/subscription |
user |
URL агрегированной подписки пользователя (все активные конфиги) |
POST /api/configs → 201 Created:
Apps — каталог приложений
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/apps |
user |
Включённые приложения, сгруппированы по ОС (для страницы инструкций) |
| GET |
/api/admin/apps |
admin |
Все приложения (вкл. выключенные) |
| POST |
/api/admin/apps |
admin |
Добавить { name, downloadUrl, operatingSystem, description?, iconUrl?, sortOrder? } |
| PUT |
/api/admin/apps/{id} |
admin |
Изменить приложение (в т.ч. isEnabled) |
| DELETE |
/api/admin/apps/{id} |
admin |
Удалить приложение |
GET /api/apps → пример:
Activation (пользователь)
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/activation/status |
user |
Статус активации + текущий Pending-запрос (если есть) |
| POST |
/api/activation/request |
user |
Запросить активацию { comment? } (напр. «я Никита») |
GET /api/configs для неактивированного пользователя вернёт пустой список; POST /api/configs
до активации → 403 (или 409 с кодом NotActivated).
Admin — Activation, Roles
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/admin/activation-requests |
admin |
Список запросов активации (фильтр по статусу) |
| POST |
/api/admin/activation-requests/{id}/approve |
admin |
Одобрить → пользователь активирован |
| POST |
/api/admin/activation-requests/{id}/reject |
admin |
Отклонить { reason? } |
| GET |
/api/admin/roles |
admin |
Список ролей с квотами |
| POST |
/api/admin/roles |
admin |
Создать роль { name, maxConfigs } |
| PUT |
/api/admin/roles/{id} |
admin |
Изменить роль (напр. maxConfigs) |
| DELETE |
/api/admin/roles/{id} |
admin |
Удалить роль (нельзя системные admin/user) |
| PATCH |
/api/admin/users/{id}/role |
admin |
Сменить роль пользователю { roleId } (ровно одна) |
| PATCH |
/api/admin/users/{id}/activation |
admin |
Активировать/деактивировать напрямую { isActivated } |
Admin — Nodes
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/admin/nodes |
admin |
Список нод + статусы |
| POST |
/api/admin/nodes |
admin |
Подключить ноду { name, baseAddress, username, password, location } |
| PUT |
/api/admin/nodes/{id} |
admin |
Изменить ноду (в т.ч. isEnabled) |
| DELETE |
/api/admin/nodes/{id} |
admin |
Удалить ноду |
| POST |
/api/admin/nodes/{id}/sync |
admin |
Пересинхронизировать inbounds с 3x-ui |
| POST |
/api/admin/nodes/{id}/probe |
admin |
Проверить доступность |
Admin — Inbounds
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/admin/inbounds |
admin |
Список inbounds (по нодам) + allowedRoleIds, displayName |
| PUT |
/api/admin/inbounds/{id}/publish |
admin |
Опубликовать/снять { isPublished, displayName?, allowedRoleIds[], maxClients? } |
Admin — Users & Stats
| Метод |
Путь |
Роль |
Описание |
| GET |
/api/admin/users |
admin |
Пользователи (пагинация, поиск) |
| PATCH |
/api/admin/users/{id}/block |
admin |
Блокировать/разблокировать { isBlocked } (при блоке — отключить конфиги в 3x-ui) |
| POST |
/api/admin/users/{id}/reset-password |
admin |
Сбросить пароль пользователю без привязки Telegram (выдать временный/задать новый) |
| GET |
/api/admin/users/{id}/configs |
admin |
Конфиги пользователя |
| DELETE |
/api/admin/configs/{id} |
admin |
Принудительно отозвать любой конфиг |
| GET |
/api/admin/stats |
admin |
Сводная статистика (пользователи, конфиги, трафик) |
| GET |
/api/admin/audit |
admin |
Журнал действий (AuditLog, пагинация, фильтры) |
Public — Subscription
| Метод |
Путь |
Роль |
Описание |
| GET |
/sub/{token} |
— |
Подписка (base64-список ссылок). Токен — либо AppUser.SubscriptionToken (все активные конфиги юзера), либо VpnConfig.SubscriptionToken (один конфиг). Без /api. |
Rate-limited; отключённые/отозванные конфиги в выдачу не попадают; неизвестный/погашенный токен → 404.
Ответ отдаёт заголовок Subscription-Userinfo (upload/download/total/expire) — клиенты
(v2rayN/Nekoray и т.п.) показывают остаток трафика/срок. Также profile-update-interval.
SignalR — Hub /hubs/panel
Авторизация — тем же JWT (query access_token или заголовок). Группы: user:{userId}, admins.
Server → Client
| Событие |
Payload |
Кому |
configTrafficUpdated |
{ configId, usedUpBytes, usedDownBytes, limitBytes } |
владельцу |
configStatusChanged |
{ configId, status } |
владельцу |
nodeStatusChanged |
{ nodeId, status, lastSyncAt } |
admins |
activationRequested |
{ requestId, userId, username, comment, createdAt } |
admins |
userActivated |
{ userId } |
владельцу |
Client → Server
MVP — клиент только слушает (группировка по пользователю на сервере при подключении по UserId из JWT).
Коды ошибок
| Код |
Когда |
| 400 |
Ошибка валидации (errors в ProblemDetails) |
| 401 |
Нет/просрочен токен |
| 403 |
Нет прав (роль/владение/не активирован/роль без доступа к инбаунду) |
| 404 |
Ресурс не найден |
| 409 |
Конфликт домена (превышена квота роли, дубликат, уже есть Pending-запрос активации) |
| 422 |
Нарушение инварианта домена |
| 429 |
Rate limit |
| 502 |
Ошибка/недоступность ноды 3x-ui (при необходимости) |