Merge branch 'dev' into patch-1
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
- блокировка пользователей, входящий список тикетов поддержки, рассылки, промокоды и просмотр логов;
|
||||
- ручная синхронизация с Remnawave;
|
||||
- редактор разрешенных настроек приложения из manifest-файла;
|
||||
- раздел **Внешний вид** для логотипа, emoji-логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
|
||||
- раздел **Внешний вид** для логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
|
||||
- раздел **Инструкции подключения** для встроенной страницы установки, поведения кнопок бота и Remnawave Subscription Page config;
|
||||
- раздел **Бэкапы** для просмотра локальных ZIP-архивов, загрузки архива и восстановления БД/compose-папки;
|
||||
- редактор JSON-каталога тарифов;
|
||||
@@ -49,7 +49,7 @@
|
||||
В manifest сейчас входят:
|
||||
|
||||
- общие параметры: язык, валюта, ссылки поддержки, документы, обязательный канал, Remnawave-доступы и поведение `/start`;
|
||||
- внешний вид и доступность Web App: название, цвет, логотип, emoji-логотип и `WEBAPP_ENABLED`;
|
||||
- внешний вид и доступность Web App: название, цвет, логотип и `WEBAPP_ENABLED`;
|
||||
- инструкции подключения: `SUBSCRIPTION_GUIDES_ENABLED`, `SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED`, чтение конфига из Remnawave Panel, JSON-переопределение и резервный путь к файлу;
|
||||
- legacy-тарифы без JSON-каталога: периоды подписки, RUB/Stars цены, реферальные бонусы и пакеты трафика;
|
||||
- платежные провайдеры: включение методов, порядок кнопок, публичные параметры и секреты YooKassa, FreeKassa, Platega, SeverPay, Wata, CryptoPay, Heleket и Stars, а также текст и иконки кнопок оплаты;
|
||||
@@ -102,9 +102,11 @@
|
||||
|
||||
## Внешний вид
|
||||
|
||||
Раздел **Внешний вид** объединяет настройки бренда и темы Web App. Логотип можно загрузить файлом или по HTTPS-ссылке; backend сохраняет файл в `data/webapp-logo/uploads` и подставляет локальный URL. Если включен emoji-логотип, картинка скрывается, а для emoji можно выбрать системный, Twemoji, Noto Color, animated Noto и другие варианты отрисовки.
|
||||
Раздел **Внешний вид** объединяет настройки бренда и темы Web App. Логотип можно загрузить файлом или по HTTPS-ссылке; backend сохраняет файл в `data/webapp-logo/uploads` и подставляет локальный URL. Если логотип не задан, показывается логотип проекта по умолчанию. Favicon генерируется из логотипа или загружается отдельно.
|
||||
|
||||
В блоке тем админка читает каталог из `WEBAPP_THEMES_DIR`, показывает встроенные и кастомные темы, позволяет выбрать текущую тему, изменить accent, включить или выключить тему для админки и настроить масштаб логотипа на главной и экране входа. Кнопка предпросмотра открывает `/home?theme_preview=<key>` и не меняет глобальную тему до сохранения.
|
||||
Этот же бренд используется в HTML-письмах. Загруженный локальный логотип встраивается в письмо как inline image (`cid:webapp-logo`), поэтому email-клиенту не нужен прямой доступ к `/webapp-uploaded-logo/...`. Логотип по публичной HTTPS-ссылке остается внешней картинкой в письме.
|
||||
|
||||
В блоке тем админка читает каталог из `WEBAPP_THEMES_DIR`, показывает встроенные и кастомные темы, позволяет выбрать текущую тему, изменить accent, включить или выключить тему для админки и настроить отдельный масштаб логотипа для desktop и mobile layout. Кнопка предпросмотра открывает `/home?theme_preview=<key>` и не меняет глобальную тему до сохранения.
|
||||
|
||||
Подробный формат `theme.json`, CSS/asset-роуты и пошаговый пайплайн создания новой темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
|
||||
@@ -122,6 +124,8 @@
|
||||
- **Premium**: названия premium-раздела RU/EN, premium Internal Squads, месячный premium-лимит и RUB/Stars пакеты premium-докупки;
|
||||
- **Устройства**: RUB/Stars пакеты докупки HWID-устройств.
|
||||
|
||||
Порядок периодов, traffic-пакетов, обычных докупок, premium-докупок и HWID-пакетов меняется перетаскиванием строк в редакторе. Этот порядок сохраняется в JSON и используется на витрине Web App и в Telegram-боте.
|
||||
|
||||
Базовые и premium Internal Squads выбираются из Remnawave через `/api/admin/panel/internal-squads`. Если панель недоступна, можно сохранить уже существующие UUID в JSON, но выпадающий список не загрузится.
|
||||
|
||||
## Практические замечания
|
||||
|
||||
@@ -44,6 +44,12 @@ BRUTE_FORCE_WINDOW_SECONDS=900
|
||||
BRUTE_FORCE_LOCK_SECONDS=900
|
||||
```
|
||||
|
||||
## Брендинг писем
|
||||
|
||||
HTML-письма используют тот же бренд, что и Mini App: название из `WEBAPP_TITLE`, accent из внешнего вида и логотип из раздела **Внешний вид**. Если логотип загружен через админку файлом, backend прикладывает его к письму как inline image (`cid:webapp-logo`), поэтому получателю не нужен доступ к внутреннему `/webapp-uploaded-logo/...`.
|
||||
|
||||
Если в качестве логотипа задан публичный `https://` URL, письмо использует его как обычный внешний `<img>`. В этом режиме некоторые почтовые клиенты могут скрыть картинку, пока получатель не разрешит загрузку внешних изображений.
|
||||
|
||||
Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL wrapper автоматически.
|
||||
|
||||
`SMTP_FROM_EMAIL` должен быть подтвержден у SMTP-провайдера, иначе письмо часто отклоняется или попадает в спам. `SMTP_FROM_NAME` можно оставить пустым, тогда используется название Web App.
|
||||
|
||||
@@ -6,6 +6,8 @@ Minishop отправляет уведомления в Telegram и на email.
|
||||
|
||||
Для уведомлений жизненного цикла подписки есть отдельный флаг `SUBSCRIPTION_EMAIL_NOTIFICATIONS_ENABLED`. Если он включен, пользовательские уведомления об окончании подписки отправляются в Telegram при наличии привязанного Telegram-аккаунта и на email при наличии привязанной почты.
|
||||
|
||||
Все HTML-письма используют общий email-шаблон с брендом из Web App: заголовком, accent-цветом и логотипом. Логотип, загруженный через раздел **Внешний вид**, отправляется как inline image, а публичный HTTPS-логотип остается внешней картинкой.
|
||||
|
||||
## Сводная таблица
|
||||
|
||||
| Событие | Получатель | Telegram | Email | Условия и ограничения |
|
||||
@@ -16,7 +18,7 @@ Minishop отправляет уведомления в Telegram и на email.
|
||||
| Успешная покупка отдельного пакета трафика | Пользователь | ✓ | ✓ | Для `traffic` / `traffic_package`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная докупка обычного трафика к тарифу | Пользователь | ✓ | ✓ | Для `topup`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка premium-трафика | Пользователь | ✓ | ✓ | Для `premium_topup`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка HWID-устройств | Пользователь | ✓ | ✓ | Отправляется после оплаты `hwid_devices` или `hwid_devices_renewal`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка HWID-устройств | Пользователь | ✓ | ✓ | Отправляется после отдельной оплаты `hwid_devices`; при продлении устройств вместе с подпиской добавляется примечание к уведомлению об успешной оплате подписки. Email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Платное повышение тарифа | Пользователь | ✓ | ✓ | Для `tariff_upgrade`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Способ оплаты YooKassa привязан | Пользователь | ✓ | ✓ | Отправляется после успешного сохранения платежного метода через webhook YooKassa; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Ошибка оплаты по webhook провайдера | Пользователь | ✓ | ✓ | Отправляется, когда платежный провайдер сообщает о неуспешном платеже; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
|
||||
+111
-14
@@ -22,6 +22,30 @@
|
||||
- [Тарифы](tariffs.md) — цены, Telegram Stars и сценарии покупки.
|
||||
- [Логи](../troubleshooting/logs.md) — проверка webhook и создания платежных ссылок.
|
||||
|
||||
## Webhook URL провайдеров
|
||||
|
||||
Все платежные webhook URL строятся от `WEBHOOK_BASE_URL` — публичного HTTPS-адреса backend/webhook-домена.
|
||||
|
||||
Это должен быть домен, который проксируется на backend-сервер вебхуков (`backend:8080`), а не frontend/Mini App домен из `SUBSCRIPTION_MINI_APP_URL`.
|
||||
|
||||
Если `WEBHOOK_BASE_URL=https://bot.example.com`, полный webhook URL получается как `https://bot.example.com` + путь из таблицы.
|
||||
|
||||
| Провайдер | URL |
|
||||
| --- | --- |
|
||||
| YooKassa | `WEBHOOK_BASE_URL` + `/webhook/yookassa` |
|
||||
| FreeKassa | `WEBHOOK_BASE_URL` + `/webhook/freekassa` |
|
||||
| Platega | `WEBHOOK_BASE_URL` + `/webhook/platega` |
|
||||
| SeverPay | `WEBHOOK_BASE_URL` + `/webhook/severpay` |
|
||||
| Wata | `WEBHOOK_BASE_URL` + `/webhook/wata` |
|
||||
| CryptoPay | `WEBHOOK_BASE_URL` + `/webhook/cryptopay` |
|
||||
| Heleket | `WEBHOOK_BASE_URL` + `/webhook/heleket` |
|
||||
| PayKilla | `WEBHOOK_BASE_URL` + `/webhook/paykilla` |
|
||||
| Telegram Stars | `WEBHOOK_BASE_URL` + `/tg/webhook` |
|
||||
|
||||
После настройки сделайте тестовый платеж и проверьте, что в логах `backend` виден входящий `POST` на нужный путь.
|
||||
|
||||
Если провайдер сообщает, что адрес недоступен, проверьте DNS, HTTPS и reverse proxy для `WEBHOOK_BASE_URL`. Путь должен начинаться с `/webhook/...` без `/api`, `/auth` и frontend-домена.
|
||||
|
||||
## YooKassa
|
||||
|
||||
YooKassa используется для рублевых оплат. Провайдер также может участвовать в сценариях автопродления period-подписок.
|
||||
@@ -29,7 +53,7 @@ YooKassa используется для рублевых оплат. Прова
|
||||
### Настройка
|
||||
|
||||
1. Включите `YOOKASSA_ENABLED`.
|
||||
2. Заполните `YOOKASSA_SHOP_ID`, `YOOKASSA_SECRET_KEY`, `YOOKASSA_RETURN_URL`.
|
||||
2. Заполните `YOOKASSA_SHOP_ID`, `YOOKASSA_SECRET_KEY` и `YOOKASSA_RETURN_URL`.
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете YooKassa.
|
||||
|
||||
### Справочник
|
||||
@@ -43,10 +67,10 @@ FreeKassa подключается как отдельный платежный
|
||||
### Настройка
|
||||
|
||||
1. Включите `FREEKASSA_ENABLED`.
|
||||
2. Заполните `FREEKASSA_MERCHANT_ID`, `FREEKASSA_FIRST_SECRET`, `FREEKASSA_SECOND_SECRET`, `FREEKASSA_API_KEY`.
|
||||
2. Заполните `FREEKASSA_MERCHANT_ID`, `FREEKASSA_FIRST_SECRET`, `FREEKASSA_SECOND_SECRET` и `FREEKASSA_API_KEY`.
|
||||
3. Проверьте настройки подписи.
|
||||
4. Скопируйте URL вебхука из админ-панели и укажите его в кабинете FreeKassa.
|
||||
5. При необходимости заполните список доверенных IP.
|
||||
5. При необходимости заполните `FREEKASSA_TRUSTED_IPS`.
|
||||
|
||||
### Справочник
|
||||
|
||||
@@ -59,11 +83,20 @@ Platega подключается как отдельный платежный п
|
||||
### Настройка
|
||||
|
||||
1. Включите `PLATEGA_ENABLED`.
|
||||
2. Укажите `PLATEGA_MERCHANT_ID` и `PLATEGA_SECRET`.
|
||||
2. Укажите `PLATEGA_BASE_URL`, `PLATEGA_MERCHANT_ID` и `PLATEGA_SECRET`.
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Platega.
|
||||
4. Проверьте `PLATEGA_RETURN_URL` и `PLATEGA_FAILED_URL`.
|
||||
5. При необходимости укажите `PLATEGA_PAYMENT_METHOD`.
|
||||
|
||||
### Дополнительные кнопки
|
||||
|
||||
- `PLATEGA_SBP_ENABLED` — отдельная кнопка СБП/карта.
|
||||
- `PLATEGA_SBP_METHOD` — ID метода для СБП/карты.
|
||||
- `PLATEGA_CRYPTO_ENABLED` — отдельная crypto-кнопка Platega.
|
||||
- `PLATEGA_CRYPTO_METHOD` — ID метода для crypto-кнопки.
|
||||
- `PAYMENT_PLATEGA_SBP_*` — текст и иконка кнопки СБП/карта.
|
||||
- `PAYMENT_PLATEGA_CRYPTO_*` — текст и иконка crypto-кнопки.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [Platega](../configuration/env-vars.md#platega)
|
||||
@@ -75,9 +108,11 @@ SeverPay подключается как отдельный платежный
|
||||
### Настройка
|
||||
|
||||
1. Включите `SEVERPAY_ENABLED`.
|
||||
2. Укажите `SEVERPAY_MID`, `SEVERPAY_TOKEN`, `SEVERPAY_BASE_URL`
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете SeverPay.
|
||||
4. При необходимости задайте `SEVERPAY_LIFETIME_MINUTES`.
|
||||
2. Укажите `SEVERPAY_BASE_URL`.
|
||||
3. Заполните `SEVERPAY_MID` и `SEVERPAY_TOKEN`.
|
||||
4. Проверьте `SEVERPAY_RETURN_URL`.
|
||||
5. Скопируйте URL вебхука из админ-панели и укажите его в кабинете SeverPay.
|
||||
6. При необходимости задайте `SEVERPAY_LIFETIME_MINUTES`.
|
||||
|
||||
### Справочник
|
||||
|
||||
@@ -116,8 +151,8 @@ CryptoPay используется для криптовалютных плат
|
||||
2. Укажите `CRYPTOPAY_TOKEN`.
|
||||
3. Выберите `CRYPTOPAY_NETWORK`: `mainnet` или `testnet`.
|
||||
4. Задайте `CRYPTOPAY_CURRENCY_TYPE`: `fiat` или `crypto`.
|
||||
5. Скопируйте URL вебхука из админ-панели и укажите его в CryptoPay.
|
||||
6. Проверьте `CRYPTOPAY_ASSET`.
|
||||
5. Проверьте `CRYPTOPAY_ASSET`, например `RUB`, `USDT` или `BTC`.
|
||||
6. Скопируйте URL вебхука из админ-панели и укажите его в CryptoPay.
|
||||
|
||||
### Проверка
|
||||
|
||||
@@ -138,10 +173,12 @@ Heleket используется для крипто-инвойсов с merchan
|
||||
1. Включите `HELEKET_ENABLED`.
|
||||
2. Укажите `HELEKET_BASE_URL`, `HELEKET_MERCHANT_ID` и `HELEKET_API_KEY`.
|
||||
3. Настройте `HELEKET_CURRENCY`.
|
||||
4. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Heleket.
|
||||
5. При необходимости задайте `HELEKET_TO_CURRENCY` и `HELEKET_NETWORK`.
|
||||
7. При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE`.
|
||||
8. Для IP-фильтрации заполните `HELEKET_TRUSTED_IPS`.
|
||||
4. При необходимости задайте `HELEKET_TO_CURRENCY` и `HELEKET_NETWORK`.
|
||||
5. Проверьте `HELEKET_RETURN_URL` и `HELEKET_SUCCESS_URL`.
|
||||
6. Настройте `HELEKET_LIFETIME_SECONDS`.
|
||||
7. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Heleket.
|
||||
8. При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE`.
|
||||
9. Для IP-фильтрации заполните `HELEKET_TRUSTED_IPS`.
|
||||
|
||||
### Ограничения
|
||||
|
||||
@@ -151,6 +188,64 @@ Heleket используется для крипто-инвойсов с merchan
|
||||
|
||||
- [Heleket](../configuration/env-vars.md#heleket)
|
||||
|
||||
## PayKilla
|
||||
|
||||
PayKilla используется для крипто-инвойсов V2 через hosted checkout `https://gopay.paykilla.com/{invoice_id}`.
|
||||
|
||||
API-запросы подписываются HMAC-SHA256. Webhook проверяется по заголовку `X-API-SIGN` и raw body.
|
||||
|
||||
### Особенности
|
||||
|
||||
- PayKilla строго валидирует текстовые поля invoice.
|
||||
- В `purpose` и `description` Minishop отправляет простой английский текст `<WEBAPP_TITLE> payment <id>`.
|
||||
- Локализованное описание платежа остается только внутри Minishop.
|
||||
- ASCII-safe sanitizer допускает ASCII-буквы, цифры, пробелы, `_`, `.`, `,`.
|
||||
|
||||
### Валюта invoice
|
||||
|
||||
Minishop создает invoice в валюте, которую PayKilla принимает в поле `currency`.
|
||||
|
||||
Если валюта тарифа входит в `PAYKILLA_INVOICE_CURRENCIES`, сумма отправляется как есть.
|
||||
|
||||
Если валюта тарифа не входит в список, сумма конвертируется в `PAYKILLA_CURRENCY`. По умолчанию рублевые тарифы конвертируются в `USD` через ExchangeRate-API с кэшем `PAYKILLA_EXCHANGE_RATE_CACHE_SECONDS`.
|
||||
|
||||
### Payload invoice
|
||||
|
||||
Payload создания invoice содержит обязательные поля `type`, `purpose`, `currency`, `totalPrice` и `paymentCurrencies`.
|
||||
|
||||
Дополнительно отправляются `clientOrderId`, `description`, `expiredAt`, `userPaysServiceFee` и `userPaysNetworkFee`.
|
||||
|
||||
Redirect URLs в PayKilla не отправляются. Завершение платежа обрабатывается через webhook.
|
||||
|
||||
### API key
|
||||
|
||||
1. В PayKilla Dashboard откройте **Settings -> API keys**.
|
||||
2. Создайте ключ типа **HMAC**.
|
||||
3. Для приема оплат включите permission **INVOICE**.
|
||||
4. Permission **WITHDRAWAL** не нужен для Minishop-платежей.
|
||||
5. Сохраните `publicKey` в `PAYKILLA_API_KEY`.
|
||||
6. Сохраните `secretKey` в `PAYKILLA_SECRET_KEY`.
|
||||
|
||||
### Webhook
|
||||
|
||||
1. В PayKilla Dashboard откройте **Settings -> Webhooks**.
|
||||
2. Скопируйте URL вебхука из админ-панели и укажите его в PayKilla.
|
||||
3. Включите минимальные события: `INVOICE_PAID`, `INVOICE_EXPIRED`.
|
||||
4. Для production также включите `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `PAYMENT_OVERPAID`, `PAYMENT_UNDERPAID`, `PAYMENT_PARTIAL`, `COMPLIANCE_FAILED`.
|
||||
5. Оставьте `PAYKILLA_VERIFY_WEBHOOK_SIGNATURE=True`.
|
||||
|
||||
### Настройка
|
||||
|
||||
1. Включите `PAYKILLA_ENABLED`.
|
||||
2. Укажите `PAYKILLA_API_KEY` и `PAYKILLA_SECRET_KEY`.
|
||||
3. Оставьте `PAYKILLA_CURRENCY=USD`, если PayKilla не принимает валюту тарифов как invoice currency.
|
||||
4. В `PAYKILLA_INVOICE_CURRENCIES` укажите валюты invoice, например `USD,EUR`.
|
||||
5. В `PAYKILLA_PAYMENT_CURRENCIES` начните с `USDTTRC`.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [PayKilla](../configuration/env-vars.md#paykilla)
|
||||
|
||||
## Telegram Stars
|
||||
|
||||
Telegram Stars используются напрямую и поддерживаются в legacy-ценах и JSON-каталоге тарифов.
|
||||
@@ -171,9 +266,11 @@ Telegram Stars используются напрямую и поддержива
|
||||
|
||||
### Ограничения
|
||||
|
||||
- Отдельный платежный webhook не нужен.
|
||||
- Stars-события приходят через webhook Telegram-бота: `WEBHOOK_BASE_URL` + `/tg/webhook`.
|
||||
- XTR/Stars-докупки не конвертируются без явно заданного курса.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [Переменные платежей](../configuration/env-vars.md#платежи)
|
||||
- [Тарифы](tariffs.md)
|
||||
- [Тарифы](tariffs.md)
|
||||
+65
-58
@@ -28,6 +28,8 @@ JSON-каталог может содержать несколько тариф
|
||||
- настройка premium-раздела: названия RU/EN, premium Internal Squads, месячный premium-лимит и пакеты докупки premium-трафика в платежной валюте/Stars;
|
||||
- настройка базового HWID-лимита и пакетов докупки устройств.
|
||||
|
||||
Порядок продаваемых вариантов управляется в админке перетаскиванием строк: это работает для периодов покупки подписки, traffic-пакетов, обычных докупок трафика, premium-докупок и HWID-пакетов. Такой же порядок сохраняется в JSON и затем используется в Web App и Telegram-боте.
|
||||
|
||||
После сохранения изменения применяются к новым запросам Web App сразу, потому что конфиг тарифов загружается из JSON при обращении. Уже созданные подписки сохраняют свой `tariff_key`; при удалении или отключении тарифа проверьте, что активные подписки с этим ключом не требуют дальнейшего продления или смены.
|
||||
|
||||
Подробности по админ-панели, правам доступа, сохранению настроек и списку разделов есть в [админ-панели](admin-panel.md).
|
||||
@@ -56,16 +58,16 @@ Legacy-поля остаются алиасами: `prices_rub`, `conversion_rat
|
||||
|
||||
Платежные провайдеры не принимают произвольный код валюты одинаково. Бот фильтрует способы оплаты и блокирует создание платежа, если текущая валюта каталога не поддерживается провайдером:
|
||||
|
||||
| Провайдер | Валюты по умолчанию |
|
||||
| --- | --- |
|
||||
| YooKassa | `RUB` |
|
||||
| WATA | `RUB`, `USD`, `EUR` |
|
||||
| FreeKassa | `RUB`, `USD`, `EUR`, `UAH`, `KZT` |
|
||||
| CryptoPay | fiat: `USD`, `EUR`, `RUB`, `BYN`, `UAH`, `GBP`, `CNY`, `KZT`, `UZS`, `GEL`, `TRY`, `AMD`, `THB`, `INR`, `BRL`, `IDR`, `AZN`, `AED`, `PLN`, `ILS`; crypto: `USDT`, `TON`, `BTC`, `ETH`, `LTC`, `BNB`, `TRX`, `USDC` |
|
||||
| Heleket | настраиваемый список `HELEKET_SUPPORTED_CURRENCIES` |
|
||||
| Platega | настраиваемый список `PLATEGA_SUPPORTED_CURRENCIES` |
|
||||
| SeverPay | настраиваемый список `SEVERPAY_SUPPORTED_CURRENCIES` |
|
||||
| Telegram Stars | `XTR`, отдельные Stars-цены |
|
||||
| Провайдер | Валюты по умолчанию |
|
||||
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| YooKassa | `RUB` |
|
||||
| WATA | `RUB`, `USD`, `EUR` |
|
||||
| FreeKassa | `RUB`, `USD`, `EUR`, `UAH`, `KZT` |
|
||||
| CryptoPay | fiat: `USD`, `EUR`, `RUB`, `BYN`, `UAH`, `GBP`, `CNY`, `KZT`, `UZS`, `GEL`, `TRY`, `AMD`, `THB`, `INR`, `BRL`, `IDR`, `AZN`, `AED`, `PLN`, `ILS`; crypto: `USDT`, `TON`, `BTC`, `ETH`, `LTC`, `BNB`, `TRX`, `USDC` |
|
||||
| Heleket | настраиваемый список `HELEKET_SUPPORTED_CURRENCIES` |
|
||||
| Platega | настраиваемый список `PLATEGA_SUPPORTED_CURRENCIES` |
|
||||
| SeverPay | настраиваемый список `SEVERPAY_SUPPORTED_CURRENCIES` |
|
||||
| Telegram Stars | `XTR`, отдельные Stars-цены |
|
||||
|
||||
В админке раздел **Система → Тарифы** показывает текущую платежную валюту и матрицу провайдеров: включен ли метод, настроен ли сервис и будет ли он доступен при выбранной валюте. Для Platega, SeverPay и Heleket список валют нужно держать в соответствии с условиями вашего мерчанта.
|
||||
|
||||
@@ -113,43 +115,43 @@ Legacy-поля остаются алиасами: `prices_rub`, `conversion_rat
|
||||
|
||||
Основные поля:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `default_tariff` | Тариф по умолчанию для первичного выбора и привязки активных подписок без `tariff_key`. |
|
||||
| `default_currency` | Валюта цен по умолчанию для JSON-каталога. По умолчанию `rub`; `stars` запрещен, потому что Stars используют отдельные цены. |
|
||||
| `tariffs[].key` | Стабильный ключ тарифа. Используется в платежах, подписках и смене тарифа. |
|
||||
| `tariffs[].names` | Названия тарифа по языкам. |
|
||||
| `tariffs[].descriptions` | Описания тарифа по языкам. |
|
||||
| `tariffs[].enabled` | Доступность тарифа на витрине. |
|
||||
| `tariffs[].squad_uuids` | Internal Squads Remnawave для пользователей тарифа. |
|
||||
| `tariffs[].premium_names` | Название premium-раздела по языкам. Используется в карточке лимита, модалке докупки premium-трафика и предупреждениях. Если поле не задано, используется `Premium-серверы` / `Premium servers`. |
|
||||
| `tariffs[].premium_squad_uuids` | Internal Squads с отдельным premium-лимитом. Ноды для учета берутся автоматически из accessible nodes этих сквадов через API панели. |
|
||||
| `tariffs[].premium_monthly_gb` | Отдельный месячный лимит трафика по premium-сквадам. `0` или отсутствие поля отключает отдельное ограничение. |
|
||||
| `tariffs[].premium_topup_packages` | Пакеты докупки premium-трафика в формате `{ "rub": [{ "gb": 10, "price": 99 }], "stars": [...] }`. Требуют `premium_squad_uuids`. |
|
||||
| `tariffs[].billing_model` | Модель тарифа: `period` или `traffic`. |
|
||||
| `tariffs[].hwid_device_limit` | Базовый лимит HWID-устройств. `0` означает безлимит, отсутствие поля использует `USER_HWID_DEVICE_LIMIT`. |
|
||||
| `tariffs[].hwid_device_packages` | Пакеты докупки устройств. `price` — legacy/monthly fallback, `prices` задаёт полную цену пакета для периодов тарифа (`"1"`, `"3"`, `"6"`, `"12"`), `min_price` задаёт минимальную цену prorate-докупки. |
|
||||
| Поле | Назначение |
|
||||
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `default_tariff` | Тариф по умолчанию для первичного выбора и привязки активных подписок без `tariff_key`. |
|
||||
| `default_currency` | Валюта цен по умолчанию для JSON-каталога. По умолчанию `rub`; `stars` запрещен, потому что Stars используют отдельные цены. |
|
||||
| `tariffs[].key` | Стабильный ключ тарифа. Используется в платежах, подписках и смене тарифа. |
|
||||
| `tariffs[].names` | Названия тарифа по языкам. |
|
||||
| `tariffs[].descriptions` | Описания тарифа по языкам. |
|
||||
| `tariffs[].enabled` | Доступность тарифа на витрине. |
|
||||
| `tariffs[].squad_uuids` | Internal Squads Remnawave для пользователей тарифа. |
|
||||
| `tariffs[].premium_names` | Название premium-раздела по языкам. Используется в карточке лимита, модалке докупки premium-трафика и предупреждениях. Если поле не задано, используется `Premium-серверы` / `Premium servers`. |
|
||||
| `tariffs[].premium_squad_uuids` | Internal Squads с отдельным premium-лимитом. Ноды для учета берутся автоматически из accessible nodes этих сквадов через API панели. |
|
||||
| `tariffs[].premium_monthly_gb` | Отдельный месячный лимит трафика по premium-сквадам. `0` или отсутствие поля отключает отдельное ограничение. |
|
||||
| `tariffs[].premium_topup_packages` | Пакеты докупки premium-трафика в формате `{ "rub": [{ "gb": 10, "price": 99 }], "stars": [...] }`. Требуют `premium_squad_uuids`. Порядок строк задает порядок premium-докупок на витрине и меняется drag&drop в админке. |
|
||||
| `tariffs[].billing_model` | Модель тарифа: `period` или `traffic`. |
|
||||
| `tariffs[].hwid_device_limit` | Базовый лимит HWID-устройств. `0` означает безлимит, отсутствие поля использует `USER_HWID_DEVICE_LIMIT`. |
|
||||
| `tariffs[].hwid_device_packages` | Пакеты докупки устройств. `price` — legacy/monthly fallback, `prices` задаёт полную цену пакета для периодов тарифа (`"1"`, `"3"`, `"6"`, `"12"`), `min_price` задаёт минимальную цену prorate-докупки. Порядок строк задает порядок HWID-докупок и меняется drag&drop в админке. |
|
||||
|
||||
Для `period`-тарифа также используются:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `monthly_gb` | Базовый месячный лимит трафика тарифа. `0` означает безлимит. |
|
||||
| `prices` | Generic-цены периодов по валютам, например `{ "usd": { "1": 4.99 } }`. |
|
||||
| `prices_rub` | Legacy-цены периодов в рублях, ключ - количество месяцев. Эквивалент `prices.rub`. |
|
||||
| `prices_stars` | Цены периодов в Telegram Stars. |
|
||||
| `referral_bonus_days_inviter` | Бонус пригласившему в днях для каждого периода. Ключ - количество месяцев, как в `enabled_periods`. |
|
||||
| `referral_bonus_days_referee` | Бонус приглашенному в днях для каждого периода. Ключ - количество месяцев, как в `enabled_periods`. |
|
||||
| `enabled_periods` | Периоды, доступные для покупки. |
|
||||
| `topup_packages` | Пакеты докупки трафика именно для этого тарифа. Если поле не задано или списки пустые, докупка для тарифа не показывается в Web App и Telegram-боте. |
|
||||
| Поле | Назначение |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `monthly_gb` | Базовый месячный лимит трафика тарифа. `0` означает безлимит. |
|
||||
| `prices` | Generic-цены периодов по валютам, например `{ "usd": { "1": 4.99 } }`. |
|
||||
| `prices_rub` | Legacy-цены периодов в рублях, ключ - количество месяцев. Эквивалент `prices.rub`. |
|
||||
| `prices_stars` | Цены периодов в Telegram Stars. |
|
||||
| `referral_bonus_days_inviter` | Бонус пригласившему в днях для каждого периода. Ключ - количество месяцев, как в `enabled_periods`. |
|
||||
| `referral_bonus_days_referee` | Бонус приглашенному в днях для каждого периода. Ключ - количество месяцев, как в `enabled_periods`. |
|
||||
| `enabled_periods` | Периоды, доступные для покупки. Порядок элементов в массиве задаёт порядок периодов на витрине (в Telegram-боте и Web App) — отсортируйте их так, как нужно показывать. В веб-админке этот порядок меняется перетаскиванием строк периодов. |
|
||||
| `topup_packages` | Пакеты докупки трафика именно для этого тарифа. Если поле не задано или списки пустые, докупка для тарифа не показывается в Web App и Telegram-боте. Порядок строк задает порядок докупок на витрине и меняется drag&drop в админке. |
|
||||
|
||||
Для `traffic`-тарифа используются:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `traffic_packages` | Пакеты трафика в GB по валютам каталога и Telegram Stars. |
|
||||
| `conversion_rate_per_gb` | Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф в валюте каталога. |
|
||||
| `conversion_rate_rub_per_gb` | Legacy-алиас для рублевых каталогов. |
|
||||
| Поле | Назначение |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `traffic_packages` | Пакеты трафика в GB по валютам каталога и Telegram Stars. Порядок пакетов в списке задаёт порядок на витрине (в Telegram-боте и Web App): сначала идут пакеты валюты каталога, затем пакеты, доступные только за Stars. В веб-админке порядок меняется перетаскиванием строк. |
|
||||
| `conversion_rate_per_gb` | Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф в валюте каталога. |
|
||||
| `conversion_rate_rub_per_gb` | Legacy-алиас для рублевых каталогов. |
|
||||
|
||||
Если у traffic-тарифа нет пакетов в `default_currency`, `conversion_rate_per_gb` обязателен.
|
||||
|
||||
@@ -278,7 +280,10 @@ limit_after = current_used + balance_after
|
||||
- полная цена HWID-пакета берется из `prices[duration_months]`; если периода нет, используется fallback `price * duration_months`;
|
||||
- фактическая цена докупки считается пропорционально оплачиваемому окну `valid_from -> valid_until` относительно периода подписки и фиксируется в платежe;
|
||||
- для Telegram Stars цена округляется вверх до целого Stars, для платежной валюты — вверх до копеек; `min_price` защищает от микроплатежей в конце периода;
|
||||
- при продлении подписки докупленные устройства не продлеваются автоматически: старая докупка действует до прежнего `end_date`, а для нового срока создается отдельная `hwid_devices_renewal`-покупка;
|
||||
- кнопка докупки устройств всегда покупает устройства только для текущей активной подписки и только до текущего срока ее действия;
|
||||
- при продлении подписки пользователь видит отдельный чекбокс продления действующих докупленных устройств; чекбокс включен по умолчанию, цена считается по текущему тарифу и добавляется в тот же платеж подписки;
|
||||
- если пользователь продлил подписку без продления устройств, старая докупка продолжает действовать до своего `valid_until`, а Web App показывает предупреждение о возможном временном возврате к базовому лимиту;
|
||||
- админские продления, промокоды и реферальные бонусы добавляют фиксированное количество дней отдельно к подписке и к действующим докупкам устройств, не склеивая даты окончания;
|
||||
- `traffic`-тарифы не показывают и не принимают докупку HWID-устройств, потому что у них нет срока подписки;
|
||||
- при смене тарифа базовый лимит берется из целевого тарифа, а неиспользованная стоимость HWID-докупок в платежной валюте конвертируется в дни нового period-тарифа или GB traffic-тарифа; XTR/Stars-докупки не конвертируются без явного курса и продолжают жить по своему `valid_until`;
|
||||
- история докупок пишется в `hwid_device_purchases`;
|
||||
@@ -292,12 +297,12 @@ limit_after = current_used + balance_after
|
||||
|
||||
Варианты расчета:
|
||||
|
||||
| Переход | Поведение |
|
||||
| --- | --- |
|
||||
| `period -> period` | Остаток оплаченных дней оценивается по legacy-полю `effective_monthly_price_rub`, где хранится месячная цена в платежной валюте каталога, затем пересчитывается в дни целевого тарифа через месячную цену целевого тарифа. Неиспользованная стоимость HWID-докупок в платежной валюте добавляется к этому расчету как дополнительные дни. Количество дней округляется вниз. |
|
||||
| `period -> period` с доплатой | Если целевой тариф дороже, может быть создан платеж `tariff_upgrade`; неиспользованная стоимость HWID-докупок в платежной валюте уменьшает сумму доплаты. После оплаты применяется целевой тариф, а конвертированные HWID-окна закрываются. |
|
||||
| `period -> traffic` | Остаток оплаченных дней и неиспользованная стоимость HWID-докупок в платежной валюте конвертируются в GB по `conversion_rate_per_gb` или минимальной цене GB из пакетов целевого тарифа. |
|
||||
| `traffic -> period` | Пользователь выбирает и оплачивает период целевого тарифа; остаток GB сохраняется как `topup_balance_bytes` поверх лимита period-тарифа. |
|
||||
| Переход | Поведение |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `period -> period` | Остаток оплаченных дней оценивается по legacy-полю `effective_monthly_price_rub`, где хранится месячная цена в платежной валюте каталога, затем пересчитывается в дни целевого тарифа через месячную цену целевого тарифа. Неиспользованная стоимость HWID-докупок в платежной валюте добавляется к этому расчету как дополнительные дни. Количество дней округляется вниз. |
|
||||
| `period -> period` с доплатой | Если целевой тариф дороже, может быть создан платеж `tariff_upgrade`; неиспользованная стоимость HWID-докупок в платежной валюте уменьшает сумму доплаты. После оплаты применяется целевой тариф, а конвертированные HWID-окна закрываются. |
|
||||
| `period -> traffic` | Остаток оплаченных дней и неиспользованная стоимость HWID-докупок в платежной валюте конвертируются в GB по `conversion_rate_per_gb` или минимальной цене GB из пакетов целевого тарифа. |
|
||||
| `traffic -> period` | Пользователь выбирает и оплачивает период целевого тарифа; остаток GB сохраняется как `topup_balance_bytes` поверх лимита period-тарифа. |
|
||||
|
||||
При смене тарифа бот меняет:
|
||||
|
||||
@@ -313,15 +318,15 @@ limit_after = current_used + balance_after
|
||||
|
||||
В платежах используются поля:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `sale_mode` | Тип продажи: `subscription`, `traffic_package`, `topup`, `premium_topup`, `tariff_upgrade`, `hwid_devices`. |
|
||||
| `tariff_key` | Ключ тарифа, к которому относится платеж. |
|
||||
| `purchased_gb` | Купленный объем GB для traffic-пакетов и докупки трафика. |
|
||||
| `purchased_hwid_devices` | Количество устройств при докупке HWID. |
|
||||
| `hwid_valid_from`, `hwid_valid_until` | Зафиксированное окно действия HWID-докупки на момент создания платежа. |
|
||||
| `hwid_pricing_period_months`, `hwid_proration_ratio`, `hwid_full_price` | Метаданные расчета цены HWID-докупки: период тарифа, коэффициент prorate и полная цена пакета для периода. |
|
||||
| `subscription_duration_months` | Количество месяцев для подписки на срок; также используется платежными обработчиками как числовое поле покупки. |
|
||||
| Поле | Назначение |
|
||||
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `sale_mode` | Тип продажи: `subscription`, `traffic_package`, `topup`, `premium_topup`, `tariff_upgrade`, `hwid_devices`. |
|
||||
| `tariff_key` | Ключ тарифа, к которому относится платеж. |
|
||||
| `purchased_gb` | Купленный объем GB для traffic-пакетов и докупки трафика. |
|
||||
| `purchased_hwid_devices` | Количество устройств при докупке HWID. |
|
||||
| `hwid_valid_from`, `hwid_valid_until` | Зафиксированное окно действия HWID-докупки на момент создания платежа. |
|
||||
| `hwid_pricing_period_months`, `hwid_proration_ratio`, `hwid_full_price` | Метаданные расчета цены HWID-докупки: период тарифа, коэффициент prorate и полная цена пакета для периода. |
|
||||
| `subscription_duration_months` | Количество месяцев для подписки на срок; также используется платежными обработчиками как числовое поле покупки. |
|
||||
|
||||
В callback и metadata платежных провайдеров `sale_mode` может передаваться с суффиксом тарифа, например `subscription@standard` или `topup@standard`. При активации платежа тариф сохраняется отдельно в `tariff_key`.
|
||||
|
||||
@@ -345,12 +350,14 @@ Remnawave ограничивает доступ при достижении `tra
|
||||
|
||||
Автопродление через YooKassa применяется к подпискам на срок. Для режима продажи трафика без JSON-каталога автопродление пропускается. Для traffic-тарифов JSON-каталога покупка является пакетом трафика, а не периодической подпиской.
|
||||
|
||||
Пробный период использует настройки `TRIAL_DURATION_DAYS`, `TRIAL_TRAFFIC_LIMIT_GB`, `TRIAL_TRAFFIC_STRATEGY` и `TRIAL_SQUAD_UUIDS`. Он не выбирает тариф из JSON-каталога, но его можно настроить на странице **Система → Тарифы** рядом с каталогом продаж. Если `TRIAL_SQUAD_UUIDS` пустой, для trial применяются squads из `USER_SQUAD_UUIDS`.
|
||||
Пробный период использует настройки `TRIAL_DURATION_DAYS`, `TRIAL_TRAFFIC_LIMIT_GB`, `TRIAL_TRAFFIC_STRATEGY` и `TRIAL_SQUAD_UUIDS`. Он не выбирает тариф из JSON-каталога, но его можно настроить на странице **Система → Тарифы** рядом с каталогом продаж. Если `TRIAL_SQUAD_UUIDS` пустой, для trial применяются squads из `USER_SQUAD_UUIDS`. Переключатель `TRIAL_WITHOUT_TELEGRAM_ENABLED` управляет активацией trial для аккаунтов без Telegram, а домены из `DISPOSABLE_EMAIL_DOMAINS` требуют привязки Telegram независимо от этого переключателя.
|
||||
|
||||
Промокоды с бонусными днями применяются к покупке period-подписки.
|
||||
|
||||
Реферальные бонусы за оплату в JSON-каталоге задаются прямо в period-тарифе рядом с ценами периода: `referral_bonus_days_inviter` для пригласившего и `referral_bonus_days_referee` для приглашенного. Ключи этих словарей - месяцы периода (`"1"`, `"3"`, `"6"`, `"12"` или любые другие периоды тарифа, например `"2"`, `"4"`, `"8"`, `"16"`). Для `traffic`-тарифов такие бонусы не применяются.
|
||||
|
||||
Приветственный бонус приглашённому (`REFERRAL_WELCOME_BONUS_DAYS`) настраивается в отдельном блоке **Реферальная программа** на странице тарифов. `REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED` разрешает или запрещает выдачу этого бонуса аккаунтам без Telegram; disposable email домены из `DISPOSABLE_EMAIL_DOMAINS` всегда требуют Telegram перед начислением.
|
||||
|
||||
Если приглашенный покупает один тариф, а пригласивший находится на другом, размер бонуса берется из тарифа и периода, который купил приглашенный. При этом подписка пригласившего только продлевается на бонусные дни: лимиты, Internal Squads и другие параметры его текущего тарифа не пересчитываются под тариф приглашенного.
|
||||
|
||||
В Web App и Telegram-меню подробные строки по периодам показываются только для legacy-режима или когда активен один period-тариф. Если включено несколько period-тарифов, Web App показывает сообщение, что бонус зависит от тарифа и периода оплаты друга, затем список тарифов с диапазонами "от N до N дней" и раскрытием подробностей по иконке вопроса. Telegram-меню в этом случае показывает только диапазоны по каждому тарифу.
|
||||
|
||||
@@ -67,7 +67,7 @@ SUPPORT_TICKET_RATE_LIMIT_PER_HOUR=5
|
||||
|
||||
Если `WEBAPP_ENABLED=False`, пользовательское веб-приложение и админ-панель не регистрируются. Чтобы снова попасть в админку, включите `WEBAPP_ENABLED=True` в `.env` и перезапустите backend/frontend контейнеры.
|
||||
|
||||
Внешний вид настраивается в админке: раздел **Внешний вид** управляет логотипом, emoji-логотипом, accent-цветом, выбранной темой и масштабом логотипа. Кастомные темы читаются из `WEBAPP_THEMES_DIR`, а `WEBAPP_DEFAULT_THEME` может принудительно выбрать тему по ключу. Подробный контракт `theme.json`, CSS/asset-роуты и пайплайн создания темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
Внешний вид настраивается в админке: раздел **Внешний вид** управляет логотипом, favicon, accent-цветом, выбранной темой и отдельным масштабом логотипа для desktop/mobile layout. Кастомные темы читаются из `WEBAPP_THEMES_DIR`, а `WEBAPP_DEFAULT_THEME` может принудительно выбрать тему по ключу. Подробный контракт `theme.json`, CSS/asset-роуты и пайплайн создания темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
|
||||
## Авторизация
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ Web App поддерживает файловые темы, предпросмо
|
||||
- включить или выключить применение темы в админ-панели;
|
||||
- настроить масштаб логотипа на главной и экране входа;
|
||||
- загрузить логотип файлом или по HTTPS-ссылке;
|
||||
- включить emoji-логотип и выбрать способ его отрисовки;
|
||||
- открыть предпросмотр темы через `/home?theme_preview=<key>`.
|
||||
|
||||
Через файлы темы можно менять намного больше:
|
||||
@@ -51,7 +50,9 @@ WEBAPP_DEFAULT_THEME=
|
||||
|
||||
В compose-примерах `data/themes` - это локальная папка рядом с выбранным `docker-compose.yml`; она монтируется в контейнер как `/app/data/themes`. Правки в `backend/bot/app/web/themes` попадают в прод только при сборке собственного образа; опубликованный образ их не видит.
|
||||
|
||||
Важно: `WEBAPP_PRIMARY_COLOR`, `WEBAPP_LOGO_URL`, `WEBAPP_LOGO_USE_EMOJI`, `WEBAPP_LOGO_EMOJI` и `WEBAPP_LOGO_EMOJI_FONT` больше не являются рабочим способом первичной настройки через `.env`. Эти значения редактируются в админке и сохраняются как overrides в базе. Тема при этом может использовать сохраненный primary color как fallback accent.
|
||||
Важно: `WEBAPP_PRIMARY_COLOR` и `WEBAPP_LOGO_URL` больше не являются рабочим способом первичной настройки через `.env`. Эти значения редактируются в админке и сохраняются как overrides в базе. Тема при этом может использовать сохраненный primary color как fallback accent.
|
||||
|
||||
Email-шаблоны берут тот же бренд из настроек внешнего вида. Загруженный логотип добавляется в письма как inline image (`cid:webapp-logo`), а публичный HTTPS-логотип остается внешней картинкой, которую почтовый клиент может скрыть до разрешения загрузки изображений.
|
||||
|
||||
## Контракт `theme.json`
|
||||
|
||||
@@ -114,7 +115,8 @@ WEBAPP_DEFAULT_THEME=
|
||||
"font_sans": "Inter, system-ui, sans-serif",
|
||||
"font_logo": "Inter, system-ui, sans-serif",
|
||||
"font_mono": "\"JetBrains Mono\", \"Fira Code\", monospace",
|
||||
"home_logo_scale": 120,
|
||||
"home_logo_scale_desktop": 120,
|
||||
"home_logo_scale_mobile": 95,
|
||||
"admin_bg": "#05040a",
|
||||
"admin_surface": "#11101c",
|
||||
"admin_surface_2": "#090815",
|
||||
@@ -130,52 +132,54 @@ WEBAPP_DEFAULT_THEME=
|
||||
|
||||
Поля верхнего уровня:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `key` | Уникальный ключ темы, 1-64 символа: латиница, цифры, `_` и `-`. Если ключ не указан, берется имя папки. |
|
||||
| `names` | Локализованные названия, например `ru` и `en`. |
|
||||
| `enabled` | Показывать тему пользователям. Отключенная тема не попадает в публичный каталог. |
|
||||
| `default` | Делает тему выбранной по умолчанию, если `WEBAPP_DEFAULT_THEME` не задан. |
|
||||
| Поле | Назначение |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `key` | Уникальный ключ темы, 1-64 символа: латиница, цифры, `_` и `-`. Если ключ не указан, берется имя папки. |
|
||||
| `names` | Локализованные названия, например `ru` и `en`. |
|
||||
| `enabled` | Показывать тему пользователям. Отключенная тема не попадает в публичный каталог. |
|
||||
| `default` | Делает тему выбранной по умолчанию, если `WEBAPP_DEFAULT_THEME` не задан. |
|
||||
| `use_primary_accent` | Если `true`, тема может получить accent из настройки внешнего вида, когда в `tokens.accent` ничего нет. |
|
||||
| `use_in_admin` | Если `false`, пользовательская часть использует тему, но админка откатывается на `dark`. |
|
||||
| `css_file` | CSS-файл внутри папки темы. Может быть `style.css` или вложенный путь вроде `css/theme.css`. |
|
||||
| `assets_version` | Версия ассетов. Для встроенных тем используется для обновления старых файлов в `data/themes`. |
|
||||
| `tokens` | Дизайн-токены, которые превращаются в CSS-переменные на `.app-shell`. |
|
||||
| `use_in_admin` | Если `false`, пользовательская часть использует тему, но админка откатывается на `dark`. |
|
||||
| `css_file` | CSS-файл внутри папки темы. Может быть `style.css` или вложенный путь вроде `css/theme.css`. |
|
||||
| `assets_version` | Версия ассетов. Для встроенных тем используется для обновления старых файлов в `data/themes`. |
|
||||
| `tokens` | Дизайн-токены, которые превращаются в CSS-переменные на `.app-shell`. |
|
||||
|
||||
## Токены
|
||||
|
||||
Поддерживаемые токены:
|
||||
|
||||
| Токен | CSS-переменная | Что меняет |
|
||||
| --- | --- | --- |
|
||||
| `color_scheme` | `color-scheme` | Нативная светлая/темная схема браузера: `dark` или `light`. |
|
||||
| `style_preset` | CSS-класс пресета | Сейчас `win95`/`windows95` добавляет `theme-preset-win95`; остальные значения не дают специального класса. |
|
||||
| `accent` | `--accent` | Главный акцент: активные элементы, кнопки, прогресс, фокус. Только hex `#RGB` или `#RRGGBB`. |
|
||||
| `bg` | `--bg` | Основной фон приложения. |
|
||||
| `panel` | `--panel` | Основные карточки и поверхности. |
|
||||
| `panel_2` | `--panel-2` | Вторичные поверхности. |
|
||||
| `panel_3` | `--panel-3` | Поверхности повышенной вложенности, dropdown/popover. |
|
||||
| `border` | `--border` | Обычные границы. |
|
||||
| `border_strong` | `--border-strong` | Усиленные границы и hover-состояния. |
|
||||
| `text` | `--text` | Основной текст. |
|
||||
| `muted` | `--muted` | Вторичный текст. |
|
||||
| `dim` | `--dim` | Еще более тихий текст и служебные подписи. |
|
||||
| `danger` | `--danger` | Ошибки и опасные действия. |
|
||||
| `blue` | `--blue` | Синий вспомогательный цвет. |
|
||||
| `radius` | `--radius` | Базовый радиус карточек, кнопок и контролов. |
|
||||
| `font_sans` | `--font-sans` | Основной шрифт интерфейса. |
|
||||
| `font_logo` | `--font-logo` | Шрифт бренда и заголовка. |
|
||||
| `font_mono` | `--font-mono` | Моноширинный шрифт. |
|
||||
| `home_logo_scale` | `--home-logo-scale` | Масштаб логотипа на главной и входе, от `50` до `300` процентов. |
|
||||
| `admin_bg` | `--admin-bg` | Фон админ-панели. |
|
||||
| `admin_surface` | `--admin-surface` | Основные карточки админки. |
|
||||
| `admin_surface_2` | `--admin-surface-2` | Вторичные поверхности админки. |
|
||||
| `admin_elev` | `--admin-elev` | Elevated-поверхности админки. |
|
||||
| `admin_border` | `--admin-border` | Границы админки. |
|
||||
| `admin_border_strong` | `--admin-border-strong` | Усиленные границы админки. |
|
||||
| `admin_text` | `--admin-text` | Основной текст админки. |
|
||||
| `admin_muted` | `--admin-muted` | Вторичный текст админки. |
|
||||
| `admin_dim` | `--admin-dim` | Тихие подписи админки. |
|
||||
| Токен | CSS-переменная | Что меняет |
|
||||
| ------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `color_scheme` | `color-scheme` | Нативная светлая/темная схема браузера: `dark` или `light`. |
|
||||
| `style_preset` | CSS-класс пресета | Сейчас `win95`/`windows95` добавляет `theme-preset-win95`; остальные значения не дают специального класса. |
|
||||
| `accent` | `--accent` | Главный акцент: активные элементы, кнопки, прогресс, фокус. Только hex `#RGB` или `#RRGGBB`. |
|
||||
| `bg` | `--bg` | Основной фон приложения. |
|
||||
| `panel` | `--panel` | Основные карточки и поверхности. |
|
||||
| `panel_2` | `--panel-2` | Вторичные поверхности. |
|
||||
| `panel_3` | `--panel-3` | Поверхности повышенной вложенности, dropdown/popover. |
|
||||
| `border` | `--border` | Обычные границы. |
|
||||
| `border_strong` | `--border-strong` | Усиленные границы и hover-состояния. |
|
||||
| `text` | `--text` | Основной текст. |
|
||||
| `muted` | `--muted` | Вторичный текст. |
|
||||
| `dim` | `--dim` | Еще более тихий текст и служебные подписи. |
|
||||
| `danger` | `--danger` | Ошибки и опасные действия. |
|
||||
| `blue` | `--blue` | Синий вспомогательный цвет. |
|
||||
| `radius` | `--radius` | Базовый радиус карточек, кнопок и контролов. |
|
||||
| `font_sans` | `--font-sans` | Основной шрифт интерфейса. |
|
||||
| `font_logo` | `--font-logo` | Шрифт бренда и заголовка. |
|
||||
| `font_mono` | `--font-mono` | Моноширинный шрифт. |
|
||||
| `home_logo_scale_desktop` | `--home-logo-scale-desktop` | Масштаб логотипа на desktop layout, от `50` до `300` процентов. |
|
||||
| `home_logo_scale_mobile` | `--home-logo-scale-mobile` | Масштаб логотипа на mobile layout, от `50` до `300` процентов. |
|
||||
| `home_logo_scale` | `--home-logo-scale` | Legacy fallback для старых тем; используется, если desktop/mobile token не задан. |
|
||||
| `admin_bg` | `--admin-bg` | Фон админ-панели. |
|
||||
| `admin_surface` | `--admin-surface` | Основные карточки админки. |
|
||||
| `admin_surface_2` | `--admin-surface-2` | Вторичные поверхности админки. |
|
||||
| `admin_elev` | `--admin-elev` | Elevated-поверхности админки. |
|
||||
| `admin_border` | `--admin-border` | Границы админки. |
|
||||
| `admin_border_strong` | `--admin-border-strong` | Усиленные границы админки. |
|
||||
| `admin_text` | `--admin-text` | Основной текст админки. |
|
||||
| `admin_muted` | `--admin-muted` | Вторичный текст админки. |
|
||||
| `admin_dim` | `--admin-dim` | Тихие подписи админки. |
|
||||
|
||||
Если `css_file` не задан, интерфейс полностью строится на токенах и общих стилях. Если `css_file` задан, токены все равно применяются первыми, а CSS темы может уточнить или полностью переопределить внешний вид.
|
||||
|
||||
@@ -254,7 +258,8 @@ CSS можно писать для пользовательской части
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url("/webapp-theme-assets/neon/icons/spark.png") center / contain no-repeat;
|
||||
background: url("/webapp-theme-assets/neon/icons/spark.png") center / contain
|
||||
no-repeat;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -302,7 +307,7 @@ CSS можно писать для пользовательской части
|
||||
|
||||
7. Подберите accent и масштаб логотипа.
|
||||
|
||||
В админке можно менять accent и `home_logo_scale` без ручного редактирования JSON. При сохранении backend перепишет `theme.json` в `WEBAPP_THEMES_DIR`, выставит ровно один `default` и сбросит кеш публичных настроек.
|
||||
В админке можно менять accent, `home_logo_scale_desktop` и `home_logo_scale_mobile` без ручного редактирования JSON. При сохранении backend перепишет `theme.json` в `WEBAPP_THEMES_DIR`, выставит ровно один `default` и сбросит кеш публичных настроек. Старый `home_logo_scale` сохраняется как fallback для уже существующих тем.
|
||||
|
||||
8. Добавьте `style.css`, если токенов мало.
|
||||
|
||||
@@ -329,7 +334,6 @@ CSS можно писать для пользовательской части
|
||||
11. Сделайте тему дефолтной.
|
||||
|
||||
Есть два способа:
|
||||
|
||||
- в админке выбрать тему и сохранить;
|
||||
- указать `WEBAPP_DEFAULT_THEME=neon` в `.env`, если нужен жесткий override на уровне окружения.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user