Админ-панель 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-файл есть, но его нельзя перезаписать, сохранение отклоняется, чтобы БД не разъехалась с главным файлом.
Формат файла:
Инструкции подключения
Заголовок раздела «Инструкции подключения»Секция Система -> Настройки -> Инструкции подключения управляет встроенным экраном установки. 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.