feat: add RBAC permission system to admin cabinet frontend

- Permission store (Zustand) with wildcard matching and role level checks
- PermissionRoute guard for route-level access control
- PermissionGate component for element-level visibility
- Admin Roles page: CRUD, permission editor, role assignment
- Admin Policies page: ABAC policy management (time/IP conditions)
- Admin Audit Log page: filterable log viewer with CSV export
- AdminPanel sidebar navigation gated by permissions
- 4 locale files updated (en, ru, zh, fa) with RBAC translations
- API client module for all RBAC endpoints
This commit is contained in:
Fringg
2026-02-25 03:02:52 +03:00
parent 48be067d1b
commit 874ee2682e
15 changed files with 5320 additions and 121 deletions

View File

@@ -884,7 +884,11 @@
"pinnedMessages": "Закреплённые",
"partners": "Партнёры",
"withdrawals": "Выводы",
"channelSubscriptions": "Обязательные каналы"
"channelSubscriptions": "Обязательные каналы",
"roles": "Роли",
"roleAssign": "Назначение ролей",
"policies": "Политики доступа",
"auditLog": "Журнал аудита"
},
"panel": {
"title": "Панель администратора",
@@ -912,7 +916,11 @@
"pinnedMessagesDesc": "Управление закреплёнными сообщениями",
"partnersDesc": "Управление заявками партнёров и комиссиями",
"withdrawalsDesc": "Проверка и обработка заявок на вывод",
"channelSubscriptionsDesc": "Управление обязательными подписками на каналы"
"channelSubscriptionsDesc": "Управление обязательными подписками на каналы",
"rolesDesc": "Управление ролями и правами администраторов",
"roleAssignDesc": "Назначение и отзыв ролей пользователей",
"policiesDesc": "Настройка политик контроля доступа ABAC",
"auditLogDesc": "Просмотр активности системы и истории доступа"
},
"trafficUsage": {
"title": "Расход трафика",
@@ -3063,7 +3071,8 @@
"marketing": "Маркетинг",
"system": "Система",
"tariffs": "Тарифы и продажи",
"users": "Пользователи"
"users": "Пользователи",
"security": "Безопасность"
},
"theme": {
"accentColor": "Акцентный цвет",
@@ -3472,6 +3481,283 @@
"unhealthy": "Проблема",
"components": "Компоненты",
"uptime": "Аптайм"
},
"roles": {
"title": "Роли и права",
"subtitle": "Управление ролями администраторов и их правами",
"createRole": "Создать роль",
"noRoles": "Роли не найдены",
"systemBadge": "Системная",
"inactiveBadge": "Неактивна",
"levelLabel": "Уровень",
"usersCount_one": "{{count}} пользователь",
"usersCount_few": "{{count}} пользователя",
"usersCount_many": "{{count}} пользователей",
"permissionsCount_one": "{{count}} разрешение",
"permissionsCount_few": "{{count}} разрешения",
"permissionsCount_many": "{{count}} разрешений",
"stats": {
"totalRoles": "Всего ролей",
"active": "Активные",
"system": "Системные"
},
"actions": {
"edit": "Редактировать",
"delete": "Удалить"
},
"modal": {
"createTitle": "Создание роли",
"editTitle": "Редактирование роли",
"close": "Закрыть"
},
"form": {
"name": "Название роли",
"namePlaceholder": "например, Модератор",
"description": "Описание",
"descriptionPlaceholder": "Для чего нужна эта роль...",
"level": "Уровень приоритета",
"levelValue": "Значение уровня",
"levelHint": "Чем выше уровень, тем больше полномочий. Пользователи могут управлять только ролями с уровнем ниже своего.",
"color": "Цвет",
"presets": "Шаблоны прав",
"permissions": "Разрешения",
"toggleSection": "Переключить все права {{section}}",
"selectedPermissions_one": "{{count}} разрешение выбрано",
"selectedPermissions_few": "{{count}} разрешения выбрано",
"selectedPermissions_many": "{{count}} разрешений выбрано",
"cancel": "Отмена",
"saving": "Сохранение...",
"save": "Сохранить"
},
"presets": {
"moderator": "Модератор",
"marketer": "Маркетолог",
"support": "Поддержка"
},
"confirm": {
"title": "Удалить роль?",
"text": "Роль будет безвозвратно удалена. Пользователи с этой ролью потеряют её права.",
"cancel": "Отмена",
"delete": "Удалить",
"deleting": "Удаление..."
},
"errors": {
"loadFailed": "Не удалось загрузить роли",
"createFailed": "Не удалось создать роль",
"updateFailed": "Не удалось обновить роль",
"nameRequired": "Укажите название роли"
}
},
"roleAssign": {
"title": "Назначение ролей",
"subtitle": "Назначение и управление ролями пользователей",
"assignSection": "Назначить роль",
"userLabel": "Пользователь",
"searchPlaceholder": "Поиск по имени, username, Telegram ID или email...",
"clearUser": "Очистить выбранного пользователя",
"noUsersFound": "Пользователи не найдены",
"roleLabel": "Роль",
"selectRole": "Выберите роль...",
"expiresLabel": "Истекает (необязательно)",
"expiresHint": "Оставьте пустым для бессрочного назначения",
"assignButton": "Назначить",
"assigning": "Назначение...",
"assignSuccess": "Роль успешно назначена",
"currentAssignments": "Текущие назначения",
"totalAssignments_one": "{{count}} назначение",
"totalAssignments_few": "{{count}} назначения",
"totalAssignments_many": "{{count}} назначений",
"noAssignments": "Назначения ролей не найдены",
"unknownUser": "Неизвестный пользователь",
"system": "Система",
"permanent": "Бессрочно",
"expired": "истекло",
"revoke": "Отозвать",
"revokeAriaLabel": "Отозвать роль {{role}} у {{user}}",
"table": {
"user": "Пользователь",
"role": "Роль",
"assignedBy": "Назначил",
"assignedAt": "Дата назначения",
"expires": "Истекает",
"actions": "Действия"
},
"pagination": {
"showing": "{{from}}-{{to}} из {{total}}",
"prev": "Предыдущая страница",
"next": "Следующая страница"
},
"confirm": {
"title": "Отозвать роль?",
"text": "Пользователь немедленно потеряет все права, связанные с этой ролью.",
"cancel": "Отмена",
"revoke": "Отозвать",
"revoking": "Отзыв..."
},
"errors": {
"userRequired": "Выберите пользователя",
"roleRequired": "Выберите роль",
"assignFailed": "Не удалось назначить роль"
}
},
"policies": {
"title": "Политики доступа",
"subtitle": "Управление политиками контроля доступа ABAC",
"createPolicy": "Создать политику",
"noPolicies": "Политики не найдены",
"inactiveBadge": "Неактивна",
"effectAllow": "Разрешить",
"effectDeny": "Запретить",
"global": "Глобальная",
"unknownRole": "Неизвестная роль",
"roleLabel": "Роль",
"priorityLabel": "Приоритет",
"stats": {
"total": "Всего политик",
"allow": "Разрешающие",
"deny": "Запрещающие",
"active": "Активные"
},
"actions": {
"edit": "Редактировать",
"delete": "Удалить"
},
"modal": {
"createTitle": "Создание политики",
"editTitle": "Редактирование политики",
"close": "Закрыть"
},
"form": {
"name": "Название политики",
"namePlaceholder": "например, Блокировка ночного доступа",
"description": "Описание",
"descriptionPlaceholder": "Что делает эта политика...",
"effect": "Эффект",
"resource": "Ресурс",
"selectResource": "Выберите ресурс...",
"actions": "Действия",
"role": "Роль (необязательно)",
"globalOption": "Глобальная (все роли)",
"roleHint": "Оставьте Глобальная, чтобы политика применялась ко всем ролям.",
"priority": "Приоритет",
"priorityHint": "Политики с более высоким приоритетом проверяются первыми. Запрет перекрывает Разрешение при равном приоритете.",
"conditions": "Условия",
"cancel": "Отмена",
"saving": "Сохранение...",
"save": "Сохранить"
},
"conditions": {
"timeRange": "Временной диапазон",
"timeStart": "Время начала",
"timeEnd": "Время окончания",
"ipWhitelist": "Белый список IP",
"ipPlaceholder": "Введите IP и нажмите Enter...",
"removeIp": "Удалить {{ip}}",
"ipCount_one": "{{count}} IP",
"ipCount_few": "{{count}} IP",
"ipCount_many": "{{count}} IP",
"rateLimit": "Лимит запросов",
"rateLimitValue": "Значение лимита запросов",
"rateValue": "{{count}}/ч",
"perHour": "действий в час"
},
"confirm": {
"title": "Удалить политику?",
"text": "Политика будет безвозвратно удалена. Правила контроля доступа будут пересчитаны.",
"cancel": "Отмена",
"delete": "Удалить",
"deleting": "Удаление..."
},
"errors": {
"loadFailed": "Не удалось загрузить политики",
"createFailed": "Не удалось создать политику",
"updateFailed": "Не удалось обновить политику",
"nameRequired": "Укажите название политики",
"resourceRequired": "Выберите ресурс",
"actionsRequired": "Выберите хотя бы одно действие"
}
},
"auditLog": {
"title": "Журнал аудита",
"subtitle": "Просмотр активности системы и истории доступа",
"back": "Назад",
"exportCsv": "Экспорт CSV",
"exporting": "Экспорт...",
"refresh": "Обновить",
"unknownUser": "Неизвестный пользователь",
"noEntries": "Записи журнала аудита не найдены",
"totalEntries_one": "{{count}} запись",
"totalEntries_few": "{{count}} записи",
"totalEntries_many": "{{count}} записей",
"autoRefresh": {
"label": "Автообновление",
"tooltip": "Автообновление каждые 30 секунд"
},
"filters": {
"title": "Фильтры",
"active": "Активны",
"action": "Действие",
"actionPlaceholder": "Поиск по действию...",
"resource": "Тип ресурса",
"allResources": "Все ресурсы",
"status": "Статус",
"allStatuses": "Все статусы",
"dateFrom": "Дата от",
"dateTo": "Дата до",
"apply": "Применить",
"clear": "Сбросить"
},
"status": {
"success": "Успешно",
"denied": "Отказано",
"error": "Ошибка"
},
"resourceTypes": {
"users": "Пользователи",
"tickets": "Тикеты",
"roles": "Роли",
"policies": "Политики",
"settings": "Настройки",
"broadcasts": "Рассылки",
"tariffs": "Тарифы",
"servers": "Серверы",
"payments": "Платежи",
"campaigns": "Кампании",
"promocodes": "Промокоды"
},
"details": {
"userAgent": "User Agent",
"requestPath": "Путь запроса",
"ipAddress": "IP-адрес",
"timestamp": "Время",
"before": "До",
"after": "После",
"fullDetails": "Полные данные"
},
"time": {
"justNow": "Только что",
"minutesAgo_one": "{{count}} мин назад",
"minutesAgo_few": "{{count}} мин назад",
"minutesAgo_many": "{{count}} мин назад",
"hoursAgo_one": "{{count}} ч назад",
"hoursAgo_few": "{{count}} ч назад",
"hoursAgo_many": "{{count}} ч назад",
"daysAgo_one": "{{count}} день назад",
"daysAgo_few": "{{count}} дня назад",
"daysAgo_many": "{{count}} дней назад"
},
"pagination": {
"pageSize": "На странице:",
"pageOf": "{{current}} / {{total}}",
"first": "Первая страница",
"previous": "Предыдущая страница",
"next": "Следующая страница",
"last": "Последняя страница"
},
"errors": {
"loadFailed": "Не удалось загрузить журнал аудита",
"retry": "Попробовать снова"
}
}
},
"profile": {