feat: add backups feature
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
- редактор разрешенных настроек приложения из manifest-файла;
|
||||
- раздел **Внешний вид** для логотипа, emoji-логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
|
||||
- раздел **Инструкции подключения** для встроенной страницы установки, поведения кнопок бота и Remnawave Subscription Page config;
|
||||
- раздел **Бэкапы** для просмотра локальных ZIP-архивов, загрузки архива и восстановления БД/compose-папки;
|
||||
- редактор JSON-каталога тарифов;
|
||||
- загрузка Internal Squads из Remnawave для выбора в тарифах.
|
||||
|
||||
@@ -58,6 +59,10 @@
|
||||
|
||||
Для каждого платежного метода в разделе провайдера доступны настройки отображения `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`) через модалку в админке.
|
||||
|
||||
## Бэкапы
|
||||
|
||||
Раздел **Система -> Бэкапы** показывает архивы из `data/backups`, принимает upload ZIP-файла и запускает восстановление БД/compose-папки с предварительной проверкой архива. Подробная эксплуатационная инструкция: [бэкапы и восстановление](backups.md).
|
||||
|
||||
## Переводы
|
||||
|
||||
Раздел **Система -> Переводы** позволяет переопределять отдельные строки из `locales/ru.json` и `locales/en.json` без монтирования полного файла локализации. Строки сгруппированы по месту применения: админка, Mini App, Telegram-бот, платежи, подписки, поддержка и другие группы.
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# Бэкапы и восстановление
|
||||
|
||||
Minishop умеет автоматически собирать ZIP-бэкапы в worker-контейнере, хранить последние архивы на сервере, отправлять их в Telegram и восстанавливать БД/compose-папку из админки.
|
||||
|
||||
## Что попадает в архив
|
||||
|
||||
Архив создается в `BACKUP_DIR`, по умолчанию `data/backups` внутри volume `shop-data`.
|
||||
|
||||
Типовой файл называется так:
|
||||
|
||||
```text
|
||||
remnawave-minishop-backup-20260527-120000+0300.zip
|
||||
```
|
||||
|
||||
Внутри:
|
||||
|
||||
- `database/<POSTGRES_DB>.dump` - `pg_dump` в custom format для `pg_restore`;
|
||||
- `compose/` - snapshot папки с `docker-compose.yml`, `.env` и соседними конфигами;
|
||||
- `manifest.json` - дата создания, сведения о БД, compose snapshot и предупреждения.
|
||||
|
||||
Если compose-папка не смонтирована или недоступна, worker не роняет весь бэкап: архив будет создан с дампом БД и предупреждением в `manifest.json`.
|
||||
|
||||
## Настройка
|
||||
|
||||
Основные параметры доступны в админке: **Система -> Настройки -> Бэкапы**.
|
||||
|
||||
Минимальный `.env`:
|
||||
|
||||
```env
|
||||
BACKUP_ENABLED=True
|
||||
BACKUP_CHAT_ID=-1001234567890
|
||||
BACKUP_INTERVAL_SECONDS=3600
|
||||
BACKUP_LOCAL_RETENTION=100
|
||||
BACKUP_COMPOSE_ENABLED=True
|
||||
COMPOSE_BACKUP_SOURCE=.
|
||||
COMPOSE_RESTORE_MODE=rw
|
||||
```
|
||||
|
||||
`BACKUP_INTERVAL_SECONDS=3600` запускает бэкапы ровно на границе часа: 12:00, 13:00 и т.д. Значение по умолчанию для локального хранения - 100 последних ZIP-архивов.
|
||||
|
||||
`BACKUP_CHAT_ID` задает чат Telegram для отправки архивов. Если он пустой, используется `LOG_CHAT_ID`. Для topic/thread можно указать `BACKUP_THREAD_ID`; если он пустой, используется `LOG_THREAD_ID`.
|
||||
|
||||
Каждый архив подписывается HMAC-подписью в `manifest.json` и содержит SHA-256 каждого файла. По умолчанию restore принимает только архивы с валидной подписью этого инстанса. Если нужен отдельный стабильный ключ подписи, задайте `BACKUP_ARCHIVE_SIGNATURE_SECRET`; если ключ пустой, используется `BOT_TOKEN`.
|
||||
|
||||
## Mount compose-папки
|
||||
|
||||
В стандартных compose-файлах есть два mount:
|
||||
|
||||
- `worker`: `${COMPOSE_BACKUP_SOURCE:-.}:/app/compose-source:ro` - только читает папку для создания snapshot;
|
||||
- `backend`: `${COMPOSE_BACKUP_SOURCE:-.}:/app/compose-source:${COMPOSE_RESTORE_MODE:-rw}` - читает список архивов и может восстановить compose-папку из админки.
|
||||
|
||||
`COMPOSE_BACKUP_SOURCE=.` означает папку рядом с текущим `docker-compose.yml`. Если compose лежит в другом месте, укажите абсолютный host-путь.
|
||||
|
||||
Если нужно запретить восстановление compose-файлов из контейнера, задайте:
|
||||
|
||||
```env
|
||||
COMPOSE_RESTORE_MODE=ro
|
||||
```
|
||||
|
||||
В этом режиме восстановление БД останется доступным, а восстановление compose-папки вернет понятную ошибку о недоступной записи.
|
||||
|
||||
## Восстановление из админки
|
||||
|
||||
Откройте **Система -> Бэкапы**. В разделе можно:
|
||||
|
||||
- выбрать архив, уже лежащий в `data/backups`;
|
||||
- загрузить ZIP-архив вручную;
|
||||
- отметить, что восстанавливать: `БД`, `compose-папка` или оба варианта;
|
||||
- запустить восстановление после подтверждения.
|
||||
|
||||
БД восстанавливается через `pg_restore --clean --if-exists --no-owner --no-privileges`. На время восстановления лучше не запускать платежи, рассылки, массовую синхронизацию и ручные изменения подписок.
|
||||
|
||||
Compose-файлы восстанавливаются поверх текущей папки. Перед заменой backend создает pre-restore snapshot текущего compose-каталога рядом с остальными архивами:
|
||||
|
||||
```text
|
||||
remnawave-minishop-compose-pre-restore-YYYYMMDD-HHMMSS+ZZZZ.zip
|
||||
```
|
||||
|
||||
После восстановления compose-папки перезапустите нужные сервисы, чтобы изменения `docker-compose.yml`, `.env`, Caddyfile/Nginx-конфигов и других файлов реально применились:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build backend worker
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Если менялись proxy-конфиги, перезапустите соответствующий сервис (`caddy`, `nginx`, `newt`).
|
||||
|
||||
## Проверка архива перед восстановлением
|
||||
|
||||
Backend валидирует архив до восстановления:
|
||||
|
||||
- файл должен быть валидным ZIP;
|
||||
- `manifest.json` должен принадлежать `remnawave-minishop` и иметь поддерживаемую версию формата;
|
||||
- HMAC-подпись manifest должна быть валидной, если `BACKUP_ARCHIVE_SIGNATURE_REQUIRED=True`;
|
||||
- SHA-256 и размер каждого файла должны совпадать с manifest;
|
||||
- выбранный server-side файл должен лежать внутри `BACKUP_DIR`, путь вида `../backup.zip` отклоняется;
|
||||
- пути внутри ZIP не могут быть абсолютными, содержать `..`, `\`, пустые сегменты или дубли;
|
||||
- архивы с подозрительно большим числом файлов, размером или zip-bomb compression ratio отклоняются;
|
||||
- для восстановления БД нужен `database/*.dump` или `database/*.backup`;
|
||||
- для восстановления compose нужны файлы внутри `compose/`;
|
||||
- compose restore стартует только если целевая папка существует и доступна на запись;
|
||||
- backup/restore защищены одним Redis lock, чтобы две операции не выполнялись одновременно.
|
||||
|
||||
Это защищает от случайной загрузки мусорного файла, zip-slip-архивов, поврежденных ZIP и структурно похожих архивов, которые не были созданы этим инстансом. Если вы сознательно восстанавливаете старый неподписанный архив, временно выставьте `BACKUP_ARCHIVE_SIGNATURE_REQUIRED=False`, восстановите архив и верните проверку обратно.
|
||||
|
||||
## Ручное восстановление БД
|
||||
|
||||
Если админка недоступна, можно восстановить дамп вручную:
|
||||
|
||||
```bash
|
||||
unzip remnawave-minishop-backup-YYYYMMDD-HHMMSS+ZZZZ.zip -d restore
|
||||
docker compose cp restore/database/remnawave_minishop.dump postgres:/tmp/remnawave_minishop.dump
|
||||
docker compose stop backend worker
|
||||
docker compose exec postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner --no-privileges /tmp/remnawave_minishop.dump'
|
||||
docker compose up -d backend worker
|
||||
```
|
||||
|
||||
После ручного восстановления проверьте миграции и healthcheck:
|
||||
|
||||
```bash
|
||||
docker compose run --rm migrate
|
||||
docker compose ps
|
||||
docker compose logs -f backend worker
|
||||
```
|
||||
|
||||
## Переменные
|
||||
|
||||
Полный справочник лежит в [переменных окружения](../configuration/env-vars.md#кеши-rate-limits-и-worker). Основные ключи:
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `BACKUP_ENABLED` | Включает периодические бэкапы. |
|
||||
| `BACKUP_CHAT_ID` / `BACKUP_THREAD_ID` | Куда отправлять архивы в Telegram. |
|
||||
| `BACKUP_INTERVAL_SECONDS` | Периодичность, по умолчанию `3600`. |
|
||||
| `BACKUP_LOCAL_RETENTION` | Сколько последних архивов хранить на сервере. |
|
||||
| `BACKUP_DIR` | Каталог ZIP-архивов. |
|
||||
| `BACKUP_ARCHIVE_SIGNATURE_REQUIRED` | Требовать валидную HMAC-подпись manifest при upload/restore. |
|
||||
| `BACKUP_ARCHIVE_SIGNATURE_SECRET` | Отдельный секрет подписи архивов; если пустой, используется `BOT_TOKEN`. |
|
||||
| `BACKUP_COMPOSE_ENABLED` | Добавлять compose snapshot. |
|
||||
| `COMPOSE_BACKUP_SOURCE` | Host-путь compose-папки для mount в контейнеры. |
|
||||
| `COMPOSE_RESTORE_MODE` | `rw` для восстановления compose из админки, `ro` для запрета записи. |
|
||||
| `BACKUP_PG_DUMP_PATH` / `BACKUP_PG_RESTORE_PATH` | Пути к `pg_dump` и `pg_restore` внутри контейнеров. |
|
||||
@@ -4,7 +4,7 @@ Minishop закрывает путь от регистрации пользов
|
||||
|
||||
## Для пользователей
|
||||
|
||||
- Регистрация через Telegram Mini App или email-код.
|
||||
- Регистрация через [Telegram Mini App](telegram-auth.md) или [email-код](email-login.md).
|
||||
- Просмотр подписки, срока действия, трафика и ссылки подключения.
|
||||
- Покупка подписки, пакетов трафика и дополнительных устройств.
|
||||
- Пробный период, промокоды и реферальные сценарии.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Вход по email
|
||||
|
||||
Email-вход позволяет пользователю зарегистрироваться или войти в Mini App без Telegram. Пользователь вводит email, получает одноразовый код и может подтвердить вход кодом или magic link из письма. После входа email можно связать с Telegram-аккаунтом в настройках профиля.
|
||||
|
||||
Аккаунты только с email не получают права администратора: админка проверяет Telegram ID из `ADMIN_IDS`.
|
||||
|
||||
## Когда форма появляется
|
||||
|
||||
Кнопка входа по email показывается только если заполнены все обязательные SMTP-настройки:
|
||||
|
||||
```ini
|
||||
SMTP_HOST=smtp-relay.brevo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=<smtp-login>
|
||||
SMTP_PASSWORD=<smtp-password-or-api-key>
|
||||
SMTP_FROM_EMAIL=no-reply@domain.com
|
||||
```
|
||||
|
||||
Если хотя бы одно из этих полей пустое, backend вернет `email_auth_enabled=false` в bootstrap, а frontend скроет email-login.
|
||||
|
||||
Для magic link также нужен корректный `SUBSCRIPTION_MINI_APP_URL`, потому что ссылка в письме строится на его основе.
|
||||
|
||||
## SMTP-настройка
|
||||
|
||||
Типовой пример:
|
||||
|
||||
```ini
|
||||
SMTP_HOST=smtp-relay.brevo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_FALLBACK_PORTS=2525,465
|
||||
SMTP_TIMEOUT_SECONDS=30
|
||||
SMTP_STARTTLS=True
|
||||
SMTP_USE_SSL=False
|
||||
SMTP_USERNAME=<smtp-login>
|
||||
SMTP_PASSWORD=<smtp-password-or-api-key>
|
||||
SMTP_FROM_EMAIL=no-reply@domain.com
|
||||
SMTP_FROM_NAME=Remnawave Minishop
|
||||
|
||||
EMAIL_CODE_TTL_SECONDS=600
|
||||
EMAIL_CODE_RESEND_SECONDS=60
|
||||
EMAIL_CODE_MAX_ATTEMPTS=5
|
||||
BRUTE_FORCE_MAX_FAILURES=5
|
||||
BRUTE_FORCE_WINDOW_SECONDS=900
|
||||
BRUTE_FORCE_LOCK_SECONDS=900
|
||||
```
|
||||
|
||||
Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL wrapper автоматически.
|
||||
|
||||
`SMTP_FROM_EMAIL` должен быть подтвержден у SMTP-провайдера, иначе письмо часто отклоняется или попадает в спам. `SMTP_FROM_NAME` можно оставить пустым, тогда используется название Web App.
|
||||
|
||||
Полный справочник переменных: [SMTP и вход по email](../configuration/env-vars.md#smtp-и-вход-по-email).
|
||||
|
||||
## Как работает вход
|
||||
|
||||
1. Пользователь вводит email в форме входа.
|
||||
2. Backend проверяет rate limit и создает одноразовый код.
|
||||
3. Письмо отправляется через SMTP. Если `SUBSCRIPTION_MINI_APP_URL` валиден, в письме также есть magic link.
|
||||
4. Пользователь вводит код в Mini App или открывает magic link.
|
||||
5. Backend создает нового email-пользователя или находит существующего по email.
|
||||
6. Если в URL был referral-параметр, он применяется к новой или существующей записи.
|
||||
7. Пользователь получает Web App-сессию.
|
||||
|
||||
Коды хранятся в базе в хешированном виде, устаревают по `EMAIL_CODE_TTL_SECONDS`, повторная отправка ограничена `EMAIL_CODE_RESEND_SECONDS`, а количество попыток ввода ограничено `EMAIL_CODE_MAX_ATTEMPTS` и общими brute-force настройками.
|
||||
|
||||
## Парольный вход
|
||||
|
||||
После подтверждения email пользователь может задать пароль в настройках профиля. Пароль хранится как PBKDF2-SHA256 hash с солью.
|
||||
|
||||
После установки пароля доступен путь:
|
||||
|
||||
```text
|
||||
https://app.domain.com/login/password
|
||||
```
|
||||
|
||||
Если парольный вход не удался, frontend предлагает fallback на обычный email-код. Установка или изменение пароля тоже подтверждается email-кодом.
|
||||
|
||||
## Привязка аккаунтов
|
||||
|
||||
В настройках профиля пользователь может:
|
||||
|
||||
- привязать email к Telegram-аккаунту через код;
|
||||
- привязать Telegram к email-аккаунту через Telegram Mini Apps `initData` или Telegram OAuth;
|
||||
- задать или сменить пароль для email-входа.
|
||||
|
||||
Если email уже принадлежит другой записи, backend выполняет безопасное объединение по существующим правилам аккаунтов и инвалидирует старые Web App-кеши.
|
||||
|
||||
## Проверка после настройки
|
||||
|
||||
1. Перезапустите backend/frontend после изменения `.env`.
|
||||
2. Откройте `https://app.domain.com/` вне Telegram.
|
||||
3. Убедитесь, что форма email-входа видна.
|
||||
4. Запросите код на тестовый адрес.
|
||||
5. Проверьте письмо, magic link и ручной ввод 6-значного кода.
|
||||
6. Проверьте логи backend, если письмо не пришло:
|
||||
|
||||
```bash
|
||||
docker compose logs -f backend
|
||||
```
|
||||
|
||||
## Частые ошибки
|
||||
|
||||
- Форма email не видна: не заполнены `SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD` или `SMTP_FROM_EMAIL`.
|
||||
- Письмо не отправляется: проверьте порт, STARTTLS/SSL режим, SMTP login/API key и подтверждение отправителя.
|
||||
- Magic link ведет не туда: исправьте `SUBSCRIPTION_MINI_APP_URL`, он должен быть публичным HTTPS URL Mini App без `/api` и `/auth`.
|
||||
- Код сразу устаревает: проверьте `EMAIL_CODE_TTL_SECONDS` и время на сервере.
|
||||
- Пользователь получает `rate_limited`: подождите `EMAIL_CODE_RESEND_SECONDS` или проверьте brute-force настройки.
|
||||
|
||||
Email-уведомления поддержки и платежей используют тот же SMTP-контур. Сценарий поддержки описан в [разделе тикетов](support.md).
|
||||
@@ -45,7 +45,7 @@
|
||||
- `SUPPORT_ADMIN_NOTIFICATION_COOLDOWN_SECONDS` - пауза для Telegram/log уведомлений;
|
||||
- `SUPPORT_ADMIN_EMAIL_COOLDOWN_SECONDS` - пауза для email-уведомлений.
|
||||
|
||||
Email-уведомления администраторам включаются через `SUPPORT_ADMIN_EMAIL_NOTIFICATIONS_ENABLED=True`. Письма отправляются только администраторам из `ADMIN_IDS`, у которых в базе есть email. Для отправки нужен рабочий SMTP-конфиг, как и для входа по email.
|
||||
Email-уведомления администраторам включаются через `SUPPORT_ADMIN_EMAIL_NOTIFICATIONS_ENABLED=True`. Письма отправляются только администраторам из `ADMIN_IDS`, у которых в базе есть email. Для отправки нужен рабочий SMTP-конфиг, как и для [входа по email](email-login.md).
|
||||
|
||||
Ответ администратора и закрытие тикета дополнительно отправляются пользователю в Telegram, если у него есть Telegram-аккаунт, и на email, если он привязан.
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Telegram-авторизация
|
||||
|
||||
Telegram-вход в Mini App работает двумя способами:
|
||||
|
||||
- внутри Telegram Mini App backend проверяет Telegram Mini Apps `initData`;
|
||||
- при открытии сайта в обычном браузере используется Telegram OAuth / OpenID Connect Authorization Code Flow с PKCE, `nonce`, callback `/auth/telegram/callback` и серверной проверкой `id_token` по JWKS Telegram.
|
||||
|
||||
`initData` не требует отдельного OAuth-секрета, но требует корректного `BOT_TOKEN`, публичного HTTPS Mini App URL и настройки Mini Apps в BotFather. OAuth нужен для входа через кнопку Telegram вне клиента Telegram и для привязки Telegram к email-аккаунту из настроек профиля.
|
||||
|
||||
## Что нужно заранее
|
||||
|
||||
Минимальные переменные:
|
||||
|
||||
```ini
|
||||
WEBAPP_ENABLED=True
|
||||
SUBSCRIPTION_MINI_APP_URL=https://app.domain.com/
|
||||
WEBAPP_SESSION_SECRET=<stable-random-secret>
|
||||
WEBAPP_AUTH_MAX_AGE_SECONDS=86400
|
||||
WEBAPP_LOGIN_TOKEN_TTL_SECONDS=600
|
||||
|
||||
TELEGRAM_OAUTH_CLIENT_ID=<client-id-from-botfather>
|
||||
TELEGRAM_OAUTH_CLIENT_SECRET=<client-secret-from-botfather>
|
||||
TELEGRAM_OAUTH_REQUEST_ACCESS=write
|
||||
```
|
||||
|
||||
`SUBSCRIPTION_MINI_APP_URL` должен быть публичным HTTPS URL именно frontend/Mini App-домена. Не добавляйте сюда `/api`, `/auth`, webhook-путь или конкретную страницу.
|
||||
|
||||
`WEBAPP_SESSION_SECRET` должен быть стабильным между рестартами, иначе Web App-сессии и OAuth state-cookie станут невалидными.
|
||||
|
||||
`WEBAPP_AUTH_MAX_AGE_SECONDS` ограничивает возраст Telegram Mini Apps `initData` и OAuth `id_token`. По умолчанию это 24 часа. Слишком маленькое значение может ломать вход на устройствах с неточными часами.
|
||||
|
||||
`WEBAPP_LOGIN_TOKEN_TTL_SECONDS` управляет TTL OAuth state, nonce и login-token. По умолчанию 10 минут.
|
||||
|
||||
`TELEGRAM_OAUTH_CLIENT_ID` можно не задавать, если client id совпадает с bot id: приложение возьмет его из префикса `BOT_TOKEN`. `TELEGRAM_OAUTH_CLIENT_SECRET` для браузерного OAuth обязателен.
|
||||
|
||||
`TELEGRAM_OAUTH_REQUEST_ACCESS=write` добавляет scope `telegram:bot_access`, чтобы бот мог написать пользователю после логина. Если это не нужно, оставьте переменную пустой. Также поддерживается `phone`, если вы осознанно запрашиваете телефон.
|
||||
|
||||
Полный справочник переменных: [Веб-приложение, внешний вид и Telegram Login](../configuration/env-vars.md#веб-приложение-внешний-вид-и-telegram-login).
|
||||
|
||||
## Настройка в BotFather
|
||||
|
||||
1. Откройте `@BotFather` -> `/mybots` -> выберите бота.
|
||||
2. В `Bot Settings` -> `Domain` укажите домен Web App без протокола и пути, например `app.domain.com`.
|
||||
3. В `Bot Settings` -> `Mini Apps` укажите URL, например `https://app.domain.com/`.
|
||||
4. В `Bot Settings` -> `Web Login` включите OpenID Connect Login, если BotFather предлагает переключение.
|
||||
5. Скопируйте client id и client secret в `TELEGRAM_OAUTH_CLIENT_ID` и `TELEGRAM_OAUTH_CLIENT_SECRET`.
|
||||
6. В `Web Login` -> `Allowed URLs` добавьте:
|
||||
|
||||
```text
|
||||
https://app.domain.com/
|
||||
https://app.domain.com/auth/telegram/callback
|
||||
```
|
||||
|
||||
После изменения `.env` перезапустите backend и frontend:
|
||||
|
||||
```bash
|
||||
docker compose up -d --force-recreate backend frontend
|
||||
```
|
||||
|
||||
## Проксирование
|
||||
|
||||
Публичный домен `SUBSCRIPTION_MINI_APP_URL` должен идти в контейнер `frontend:80`. Frontend nginx сам проксирует `/api/*` и `/auth/*` во внутренний WebApp-сервер backend на `backend:8081`.
|
||||
|
||||
Если используете собственный reverse proxy, не отправляйте `/auth/telegram/start` и `/auth/telegram/callback` напрямую в webhook-сервер `backend:8080`: эти маршруты принадлежат Web App API на `backend:8081` и штатно проходят через frontend.
|
||||
|
||||
Готовые схемы Caddy, Nginx, Newt и прямой публикации описаны в [развертывании](../getting-started/deployment.md#готовые-папки-запуска).
|
||||
|
||||
## Как проверить
|
||||
|
||||
Внутри Telegram:
|
||||
|
||||
1. Откройте Mini App кнопкой бота или через URL, настроенный в BotFather.
|
||||
2. Проверьте, что пользователь входит без OAuth-redirect и видит личный кабинет.
|
||||
3. Если вход не проходит, проверьте `SUBSCRIPTION_MINI_APP_URL`, домен BotFather и возраст `initData`.
|
||||
|
||||
В обычном браузере:
|
||||
|
||||
1. Откройте `https://app.domain.com/`.
|
||||
2. Нажмите вход через Telegram.
|
||||
3. Проверьте redirect на Telegram OAuth и возврат на `https://app.domain.com/auth/telegram/callback`.
|
||||
4. После успешного callback пользователь должен вернуться на `/` со статусом `telegram_auth=success`, который frontend очистит из URL.
|
||||
|
||||
Для диагностики полезны:
|
||||
|
||||
```bash
|
||||
curl -i https://app.domain.com/auth/telegram/start
|
||||
docker compose logs -f backend frontend
|
||||
```
|
||||
|
||||
## Частые ошибки
|
||||
|
||||
- `telegram_oauth_not_configured` или `telegram_auth=not_configured`: не задан `TELEGRAM_OAUTH_CLIENT_SECRET` или client id не удалось получить из `TELEGRAM_OAUTH_CLIENT_ID`/`BOT_TOKEN`.
|
||||
- `Telegram OAuth nonce mismatch`: сессия/state устарели, поменялся `WEBAPP_SESSION_SECRET`, пользователь открыл старую вкладку или callback пришел с другого домена.
|
||||
- `Telegram OAuth ID token is stale`: `WEBAPP_AUTH_MAX_AGE_SECONDS` слишком маленький или на сервере/клиенте сбито время.
|
||||
- `Telegram OAuth callback failed`: проверьте allowed URL в BotFather и что `/auth/*` доходит до frontend/WebApp API.
|
||||
- Mini App не открывается внутри Telegram: домен в BotFather должен совпадать с `SUBSCRIPTION_MINI_APP_URL`, а URL должен быть HTTPS.
|
||||
|
||||
Общие логи по авторизации собраны в [разделе диагностики логов](../troubleshooting/logs.md#авторизация-mini-app-и-telegram-oauth).
|
||||
@@ -38,20 +38,6 @@ WEBAPP_SESSION_TTL_SECONDS=86400
|
||||
WEBAPP_AUTH_MAX_AGE_SECONDS=86400
|
||||
WEBAPP_LOGIN_TOKEN_TTL_SECONDS=600
|
||||
|
||||
TELEGRAM_OAUTH_CLIENT_ID=<client-id-from-botfather>
|
||||
TELEGRAM_OAUTH_CLIENT_SECRET=<client-secret-from-botfather>
|
||||
TELEGRAM_OAUTH_REQUEST_ACCESS=write
|
||||
|
||||
SMTP_HOST=smtp-relay.brevo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_FALLBACK_PORTS=2525,465
|
||||
SMTP_STARTTLS=True
|
||||
SMTP_USE_SSL=False
|
||||
SMTP_USERNAME=<smtp-login>
|
||||
SMTP_PASSWORD=<smtp-password-or-key>
|
||||
SMTP_FROM_EMAIL=no-reply@domain.com
|
||||
SMTP_FROM_NAME=Remnawave Minishop
|
||||
|
||||
SUPPORT_LINK=https://t.me/your_support_link
|
||||
SUPPORT_TICKETS_ENABLED=True
|
||||
SUPPORT_TICKET_RATE_LIMIT_PER_HOUR=5
|
||||
@@ -83,43 +69,17 @@ SUPPORT_TICKET_RATE_LIMIT_PER_HOUR=5
|
||||
|
||||
Внешний вид настраивается в админке: раздел **Внешний вид** управляет логотипом, emoji-логотипом, accent-цветом, выбранной темой и масштабом логотипа. Кастомные темы читаются из `WEBAPP_THEMES_DIR`, а `WEBAPP_DEFAULT_THEME` может принудительно выбрать тему по ключу. Подробный контракт `theme.json`, CSS/asset-роуты и пайплайн создания темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
|
||||
Если SMTP-настройки не заполнены, вход по email скрывается.
|
||||
## Авторизация
|
||||
|
||||
Mini App поддерживает вход через Telegram Mini Apps `initData`, Telegram OAuth / OpenID Connect вне Telegram и email-код. Подробная настройка вынесена в отдельные разделы:
|
||||
|
||||
- [Telegram-авторизация](telegram-auth.md) - BotFather, Mini Apps, Web Login, callback `/auth/telegram/callback`, OAuth-переменные и типичные ошибки.
|
||||
- [Вход по email](email-login.md) - SMTP, одноразовые коды, magic link, парольный вход и проверки доставки писем.
|
||||
|
||||
Если SMTP-настройки не заполнены, вход по email скрывается. Если Telegram OAuth не настроен, вход через Telegram продолжает работать внутри Telegram Mini App через `initData`, но внешняя браузерная авторизация не сможет стартовать.
|
||||
|
||||
Тикеты поддержки включаются через `SUPPORT_TICKETS_ENABLED`; внешний резервный контакт задается `SUPPORT_LINK`. Полный сценарий пользователя, админа и уведомлений описан в разделе [поддержка пользователей / тикеты](support.md).
|
||||
|
||||
## Telegram-авторизация
|
||||
|
||||
Внутри Telegram Mini App пользователь авторизуется через Telegram Mini Apps `initData`. При открытии страницы вне Telegram используется Telegram OAuth / OpenID Connect Authorization Code Flow с PKCE, callback `/auth/telegram/callback`, `nonce` и серверной проверкой `id_token` по JWKS Telegram.
|
||||
|
||||
Настройка в BotFather:
|
||||
|
||||
1. Откройте `@BotFather` -> `/mybots` -> выберите бота.
|
||||
2. В `Bot Settings` -> `Domain` укажите домен Web App без протокола и пути, например `app.domain.com`.
|
||||
3. В `Bot Settings` -> `Mini Apps` укажите URL, например `https://app.domain.com/`.
|
||||
4. В `Bot Settings` -> `Web Login` включите OpenID Connect Login, если BotFather предлагает переключение.
|
||||
5. Скопируйте идентификатор клиента и секрет клиента в `TELEGRAM_OAUTH_CLIENT_ID` и `TELEGRAM_OAUTH_CLIENT_SECRET`.
|
||||
6. В `Web Login` -> `Allowed URLs` добавьте:
|
||||
|
||||
```text
|
||||
https://app.domain.com/
|
||||
https://app.domain.com/auth/telegram/callback
|
||||
```
|
||||
|
||||
`TELEGRAM_OAUTH_REQUEST_ACCESS=write` разрешает боту написать пользователю после логина. Если дополнительные разрешения не нужны, оставьте переменную пустой.
|
||||
|
||||
## Вход по email
|
||||
|
||||
Вход по email работает через одноразовый код:
|
||||
|
||||
1. Пользователь вводит email.
|
||||
2. Бот отправляет код через SMTP.
|
||||
3. Код вводится в модальном окне Web App.
|
||||
4. После подтверждения создается или находится пользователь, а email можно связать с Telegram-аккаунтом.
|
||||
|
||||
Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL.
|
||||
|
||||
Полный список переменных, обязательные поля для включения входа по email и типичные ошибки подключения описаны в разделе **SMTP и вход по email** в [configuration.md](../getting-started/configuration.md).
|
||||
|
||||
## Проксирование
|
||||
|
||||
Рекомендуемая продакшен-схема - два публичных домена:
|
||||
|
||||
Reference in New Issue
Block a user