Перейти к содержимому

Админ-панель Web App

Админ-панель доступна пользователям, чей Telegram ID указан в ADMIN_IDS. В Web App такие пользователи видят раздел администрирования; все API /api/admin/* дополнительно проверяют сессию и ADMIN_IDS на сервере. Доступ проверяется по Telegram ID, сохранённому у записи пользователя в базе: аккаунт только с email без привязки Telegram не получит права администратора, даже если его идентификатор ошибочно указан в ADMIN_IDS.

  • дашборд со статистикой пользователей, платежей и синхронизации с Remnawave;
  • список пользователей с поиском, фильтрами и колонкой premium-трафика; карточка пользователя с активной подпиской, обычным и premium-трафиком, платежами и действиями (подробнее в разделе «Пользователи» ниже);
  • блокировка пользователей, входящий список тикетов поддержки, рассылки, промокоды и просмотр логов;
  • ручная синхронизация с Remnawave;
  • редактор разрешенных настроек приложения из manifest-файла;
  • раздел Внешний вид для логотипа, emoji-логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
  • раздел Инструкции подключения для встроенной страницы установки, поведения кнопок бота и Remnawave Subscription Page config;
  • редактор JSON-каталога тарифов;
  • загрузка Internal Squads из Remnawave для выбора в тарифах.

Раздел Пользователи — таблица с пагинацией по 25 записей. Строка поиска ищет по внутреннему числовому ID, Telegram ID, фрагменту @username, имени или email; применение — кнопка «Найти» или клавиша Enter в поле поиска.

Фильтры:

  • состояние аккаунта: все / не забанены / забанены;
  • наличие Telegram или email;
  • связь с панелью Remnawave (panel_user_uuid);
  • статус подписки в панели для активной подписки в базе бота: active, expired, limited;
  • premium-трафик: все; без лимита premium в тарифе; безлимитный оверрайд; норма (ниже 85% квоты); предупреждение (от 85% до 100%); исчерпана квота.

Сортировка: по дате регистрации, имени, ID, а также по доле использования premium-квоты (если у активной подписки есть конечный premium-лимит).

В колонке premium для конечной квоты показывается израсходовано относительно лимита; лимит складывается из базовой premium-квоты тарифа, докупок и бонусов. Отображение не опирается на служебный флаг «limited» в базе как на признак «есть premium-трафик» — он отражает другую семантику на стороне панели.

Карточка пользователя по переходу из списка объединяет просмотр подписки, трафика, платежей и действий (блокировка, сообщения, продление, оверрайды и т.д.) в соответствии с доступными эндпоинтами /api/admin/users/*.

Раздел Система -> Настройки позволяет менять приложение без редактирования .env, но только в пределах allowlist из backend/bot/app/web/admin_settings_manifest.py. Даже администратор не может менять через UI произвольные атрибуты Settings.

Изменения сохраняются как overrides в базе данных и применяются поверх .env без перезапуска. Если значение сброшено, снова используется значение из .env. После сохранения публичный кеш настроек Web App сбрасывается, поэтому пользователи видят изменения при следующих запросах.

В manifest сейчас входят:

  • общие параметры: язык, валюта, ссылки поддержки, документы, обязательный канал, Remnawave-доступы и поведение /start;
  • внешний вид и доступность Web App: название, цвет, логотип, emoji-логотип и WEBAPP_ENABLED;
  • инструкции подключения: SUBSCRIPTION_GUIDES_ENABLED, SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED, чтение конфига из Remnawave Panel, JSON-override и fallback-путь к файлу;
  • legacy-цены без JSON-каталога: периоды подписки, RUB/Stars цены и пакеты трафика;
  • платежные провайдеры: включение методов, порядок кнопок, публичные параметры и секреты YooKassa, FreeKassa, Platega, SeverPay, Wata, CryptoPay, Heleket и Stars, а также текст и иконки кнопок оплаты;
  • пробный период, реферальные бонусы, уведомления, логирование, поддержка, раздел устройств, лимит устройств и legacy-лимиты трафика.

Секретные поля помечены как secret и не должны использоваться для произвольного просмотра старых значений. Настройки, которых нет в manifest, остаются только в .env или коде.

Для каждого платежного метода в разделе провайдера доступны presentation-настройки PAYMENT_<METHOD>_WEBAPP_LABEL_RU, PAYMENT_<METHOD>_WEBAPP_LABEL_EN, PAYMENT_<METHOD>_WEBAPP_ICON, PAYMENT_<METHOD>_TELEGRAM_LABEL_RU, PAYMENT_<METHOD>_TELEGRAM_LABEL_EN и PAYMENT_<METHOD>_TELEGRAM_EMOJI. Пустое значение возвращает мультиязычный дефолт из модуля платежного провайдера. Иконка Web App выбирается из уже подключённых lucide-иконок (frontend/src/lib/components/ui/icons.js) через модалку в админке.

Раздел Система -> Переводы позволяет переопределять отдельные строки из locales/ru.json и locales/en.json без монтирования полного файла локализации. Строки сгруппированы по месту применения: админка, Mini App, Telegram-бот, платежи, подписки, поддержка и другие группы.

В разделе строки разделены по аудитории: пользовательские тексты Mini App/бота/платежей и внутренние тексты админки, логов и синхронизации. Дополнительные языки можно добавлять прямо в интерфейсе по коду локали, например uk, de или pt-BR; для таких языков оверрайды хранятся без отдельного базового файла локали, а отсутствующие строки берутся из языка по умолчанию.

Файл data/locales-overrides.json считается источником правды, а таблица locale_overrides хранит его DB-зеркало. Если валидный JSON-файл есть, при старте backend и worker полностью синхронизируют БД с файлом, включая удаления строк. Если файл отсутствует, но каталог доступен для записи, он автоматически создается из текущих DB-оверрайдов или как пустой {}. Если файл не примонтирован, недоступен или временно сломан по JSON, используется fallback из БД. Сохранение из админки записывает полный итоговый снапшот и в JSON-файл, и в БД; если активный JSON-файл есть, но его нельзя перезаписать, сохранение отклоняется, чтобы БД не разъехалась с главным файлом.

Формат файла:

{
  "ru": {
    "menu_personal_account_button": "Личный кабинет"
  },
  "en": {
    "menu_personal_account_button": "Account"
  }
}

Секция Система -> Настройки -> Инструкции подключения управляет встроенным экраном установки. SUBSCRIPTION_GUIDES_ENABLED включает /install в личном кабинете, а SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED заставляет кнопки подключения в Telegram-боте открывать Mini App вместо финальной Remnawave Subscription Page. Оба переключателя включены по умолчанию.

По умолчанию Minishop читает Remnawave Subscription Page config из панели (SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED=True). Это основной режим, потому что один и тот же конфиг используется и в панели, и во встроенной инструкции. JSON-поле SUBSCRIPTION_PAGE_CONFIG_JSON применяется только когда явно включен SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED; иначе оно может храниться в админке, но не влияет на пользователей. SUBSCRIPTION_PAGE_CONFIG_PATH остается fallback-путем к локальному v1 JSON-файлу, если конфиг панели отключен или недоступен.

При сохранении backend валидирует JSON-override как Remnawave Subscription Page v1 config. Ошибки показываются как обычные validation errors настроек, а если рабочий конфиг недоступен, пользовательская кнопка подключения откатывается к старой финальной ссылке подписки.

Раздел Коммуникации -> Поддержка показывает входящий список тикетов из Mini App. В списке доступны фильтры по статусу, приоритету, категории и назначенному администратору, поиск по теме и пользователю, сортировка по обновлению, созданию или важности.

В карточке тикета администратор видит диалог, пользовательский контекст и действия: ответить пользователю, оставить внутреннюю заметку, изменить статус, приоритет, категорию или исполнителя, закрыть тикет и перейти в карточку пользователя. Внутренние заметки не показываются пользователю.

Счетчик непрочитанных обращений отображается в навигации админки. Уведомления о новых тикетах и ответах пользователя настраиваются через LOG_SUPPORT, LOG_SUPPORT_THREAD_ID и параметры SUPPORT_*. Подробности: support.md.

Раздел Внешний вид объединяет настройки бренда и темы Web App. Логотип можно загрузить файлом или по HTTPS-ссылке; backend сохраняет файл в data/webapp-logo/uploads и подставляет локальный URL. Если включен emoji-логотип, картинка скрывается, а для emoji можно выбрать системный, Twemoji, Noto Color, animated Noto и другие варианты отрисовки.

В блоке тем админка читает каталог из WEBAPP_THEMES_DIR, показывает встроенные и кастомные темы, позволяет выбрать текущую тему, изменить accent, включить или выключить тему для админки и настроить масштаб логотипа на главной и экране входа. Кнопка предпросмотра открывает /home?theme_preview=<key> и не меняет глобальную тему до сохранения.

Подробный формат theme.json, CSS/asset-роуты и пошаговый пайплайн создания новой темы описаны в webapp-themes.md.

Раздел Система -> Тарифы работает с файлом TARIFFS_CONFIG_PATH (по умолчанию data/tariffs.json). При сохранении backend валидирует payload через TariffsConfig, пишет JSON в UTF-8 и сбрасывает кеш публичных данных Web App.

Если файла еще нет, админка открывает пустой каталог. Перед сохранением должен быть хотя бы один включенный тариф, а default_tariff должен ссылаться на включенный тариф.

Редактор тарифа разделен на вкладки:

  • Основное: ключ, модель period/traffic, видимость, названия и описания RU/EN, базовые Internal Squads, HWID-лимит, месячный лимит или курс конвертации;
  • Цены: периоды и цены для period, пакеты GB и цены для traffic;
  • Докупки: обычные пакеты докупки трафика для period; для traffic отдельные докупки не нужны, пользователь повторно покупает пакеты из traffic_packages;
  • Premium: названия premium-раздела RU/EN, premium Internal Squads, месячный premium-лимит и RUB/Stars пакеты premium-докупки;
  • Устройства: RUB/Stars пакеты докупки HWID-устройств.

Базовые и premium Internal Squads выбираются из Remnawave через /api/admin/panel/internal-squads. Если панель недоступна, можно сохранить уже существующие UUID в JSON, но выпадающий список не загрузится.

Не меняйте key опубликованного тарифа без миграции активных подписок: подписки, платежи и смена тарифа ссылаются на этот ключ.

Отключенный тариф исчезает с витрины, но активные подписки с его tariff_key продолжают существовать. Удаление тарифа безопасно только если не осталось активных подписок, которым нужна докупка, продление или смена с этого тарифа.

Для Docker важно, чтобы путь TARIFFS_CONFIG_PATH был доступен на запись контейнеру. Если каталог ./data смонтирован в /app/data, админка может сохранять data/tariffs.json.