feat: add backups feature

This commit is contained in:
3252a8
2026-05-27 13:53:30 +03:00
parent e90988ea5c
commit 3aede8fe95
55 changed files with 3032 additions and 70 deletions
+25
View File
@@ -49,6 +49,8 @@
Обычно эти значения не требуют правки.
Настройки `BACKUP_*` управляют автоматическими бэкапами и восстановлением. Практический сценарий, mount compose-папки и проверки архивов описаны в [разделе про бэкапы](../features/backups.md).
| Переменная | Назначение |
| --- | --- |
| `WEBAPP_ME_CACHE_TTL_SECONDS` | TTL кеша `/api/me`. |
@@ -71,6 +73,25 @@
| `TARIFF_WORKER_LOCK_TTL_SECONDS` | TTL Redis lock для tariff worker. |
| `TARIFF_WORKER_TICK_SECONDS` | Интервал tariff worker. |
| `TARIFF_WORKER_BULK_PANEL_FETCH_THRESHOLD` | Порог активных подписок для bulk fetch пользователей панели. |
| `BACKUP_ENABLED` | Включает периодические бэкапы в worker-контейнере. По умолчанию `False`. |
| `BACKUP_INTERVAL_SECONDS` | Интервал между бэкапами. По умолчанию `3600`; запуск выравнивается на границу часа: 12:00, 13:00 и т.д. |
| `BACKUP_CHAT_ID` | Telegram chat ID для архивов. Если пусто, используется `LOG_CHAT_ID`. |
| `BACKUP_THREAD_ID` | Topic/thread ID для архивов. Если пусто, используется `LOG_THREAD_ID`. |
| `BACKUP_DIR` | Локальная папка архивов внутри контейнера. По умолчанию `data/backups` в volume `shop-data`. |
| `BACKUP_LOCAL_RETENTION` | Сколько локальных ZIP-архивов хранить после отправки. По умолчанию `100`. |
| `BACKUP_POSTGRES_DUMP_ENABLED` | Добавлять в архив `pg_dump` базы PostgreSQL. |
| `BACKUP_PG_DUMP_PATH` | Путь к `pg_dump` внутри worker-контейнера. |
| `BACKUP_PG_DUMP_TIMEOUT_SECONDS` | Таймаут выполнения `pg_dump`. |
| `BACKUP_PG_RESTORE_PATH` | Путь к `pg_restore` внутри backend-контейнера для восстановления из админки. |
| `BACKUP_PG_RESTORE_TIMEOUT_SECONDS` | Таймаут выполнения `pg_restore`. |
| `BACKUP_ARCHIVE_SIGNATURE_REQUIRED` | Требовать валидную HMAC-подпись `manifest.json` при upload/restore. По умолчанию `True`. |
| `BACKUP_ARCHIVE_SIGNATURE_SECRET` | Отдельный секрет подписи backup-архивов. Если пусто, используется `BOT_TOKEN`. |
| `BACKUP_COMPOSE_ENABLED` | Добавлять snapshot compose-каталога в архив. Если mount отсутствует, бэкап БД не падает. |
| `BACKUP_COMPOSE_SOURCE_DIR` | Путь внутри контейнера к compose-каталогу. В стандартном compose это `/app/compose-source`. |
| `BACKUP_COMPOSE_RESTORE_DIR` | Куда восстанавливать compose-файлы. Если пусто, используется `BACKUP_COMPOSE_SOURCE_DIR`. |
| `BACKUP_COMPOSE_EXCLUDE_DIRS` | Имена директорий, которые не попадают в compose snapshot. |
| `COMPOSE_BACKUP_SOURCE` | Host-путь, который Docker Compose монтирует как `/app/compose-source`. По умолчанию текущая папка compose-файла. |
| `COMPOSE_RESTORE_MODE` | Режим mount для backend: `rw` позволяет восстанавливать compose-папку из админки, `ro` оставляет только чтение. |
## Общие настройки
@@ -109,6 +130,8 @@
Часть внешнего вида (`WEBAPP_PRIMARY_COLOR`, `WEBAPP_LOGO_*`, `WEBAPP_FAVICON_*`) сохранена для совместимости, но env-значения этих полей игнорируются при загрузке. Настраивайте их в **Админка -> Внешний вид**.
Практическая настройка Mini App вынесена в [веб-приложение](../features/web-app.md), а вход через Telegram - в [Telegram-авторизацию](../features/telegram-auth.md).
| Переменная | Где менять | Назначение |
| --- | --- | --- |
| `WEBAPP_ENABLED` | `.env` / админка | Включает Web App. Если `False`, пользовательский Web App и админка недоступны до включения через `.env` и рестарта. |
@@ -143,6 +166,8 @@
Вход по email появляется только если заполнены `SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD` и `SMTP_FROM_EMAIL`.
Практический сценарий настройки SMTP, magic link и парольного входа описан в [разделе входа по email](../features/email-login.md).
| Переменная | Назначение |
| --- | --- |
| `SMTP_HOST` | SMTP host. |
+5
View File
@@ -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-бот, платежи, подписки, поддержка и другие группы.
+142
View File
@@ -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` внутри контейнеров. |
+1 -1
View File
@@ -4,7 +4,7 @@ Minishop закрывает путь от регистрации пользов
## Для пользователей
- Регистрация через Telegram Mini App или email-код.
- Регистрация через [Telegram Mini App](telegram-auth.md) или [email-код](email-login.md).
- Просмотр подписки, срока действия, трафика и ссылки подключения.
- Покупка подписки, пакетов трафика и дополнительных устройств.
- Пробный период, промокоды и реферальные сценарии.
+108
View File
@@ -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).
+1 -1
View File
@@ -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, если он привязан.
+98
View File
@@ -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).
+8 -48
View File
@@ -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).
## Проксирование
Рекомендуемая продакшен-схема - два публичных домена:
+3 -1
View File
@@ -105,6 +105,8 @@ docker compose exec backend sh -lc 'id; touch /app/data/themes/test && rm /app/d
- [configuration/env-vars.md](../configuration/env-vars.md) - полный справочник переменных `.env`.
- [features/admin-panel.md](../features/admin-panel.md) - как устроены overrides и allowlist настроек.
- [features/tariffs.md](../features/tariffs.md) - JSON-каталог тарифов и редактор тарифов.
- [Веб-приложение / Mini App](../features/web-app.md) - домен Mini App, Telegram OAuth и вход по email.
- [Веб-приложение / Mini App](../features/web-app.md) - домен Mini App, инструкции установки и проксирование.
- [Telegram-авторизация](../features/telegram-auth.md) - BotFather, Mini Apps и OAuth.
- [Вход по email](../features/email-login.md) - SMTP, коды, magic link и парольный вход.
- [Поддержка пользователей / тикеты](../features/support.md) - тикеты поддержки и уведомления.
- [Развертывание](deployment.md) - Docker Compose, обратный прокси, Caddy/Nginx и обновления.
+2
View File
@@ -356,6 +356,8 @@ docker compose exec backend sh -lc 'id; touch /app/data/themes/test && rm /app/d
## Резервная копия PostgreSQL
Для штатных автоматических ZIP-бэкапов, отправки в Telegram и восстановления через админку используйте раздел [бэкапы и восстановление](../features/backups.md). Команды ниже - минимальный ручной fallback для PostgreSQL.
```bash
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > backup.sql
```
+2 -2
View File
@@ -14,5 +14,5 @@ Remnawave Minishop состоит из Telegram-бота, backend API, worker-п
- [Установка](setup.md) - базовый запуск через Compose.
- [Развертывание](../deployment.md) - Docker Compose, Caddy, Nginx, Pangolin/Newt и запуск без обратного прокси.
- [Настройка Telegram бота](../features/web-app.md#telegram-авторизация) - Telegram OAuth и Telegram Mini App.
- [Настройка SMTP](../features/web-app.md#вход-по-email) - Вход и регистрация по email.
- [Настройка Telegram бота](../features/telegram-auth.md) - Telegram OAuth и Telegram Mini App.
- [Настройка SMTP](../features/email-login.md) - Вход и регистрация по email.
+2 -2
View File
@@ -39,8 +39,8 @@ docker compose up -d
## Настройки для веб апп
- [Настройка Telegram бота](../features/web-app.md#telegram-авторизация) - Telegram OAuth и Telegram Mini App.
- [Настройка SMTP](../features/web-app.md#вход-по-email) - Вход и регистрация по email.
- [Настройка Telegram бота](../features/telegram-auth.md) - Telegram OAuth и Telegram Mini App.
- [Настройка SMTP](../features/email-login.md) - Вход и регистрация по email.
## После первого входа
+2 -2
View File
@@ -8,5 +8,5 @@ Remnawave Minishop - Telegram-бот и Mini App для продажи и упр
- **Продажа подписок** - тарифы на срок и по трафику, докупки трафика, HWID-устройства, [premium-сквады](features/tariffs.md#premium-сквады-и-отдельный-лимит)
- **Жизненный цикл пользователей** - регистрация, пробный период, продление, синхронизация с панелью и предупреждения по трафику.
- **Mini App** - личный кабинет, инструкции установки, Telegram OAuth, вход по email и публичные реферальные ссылки.
- **Операционные инструменты** - админка, тикеты поддержки, промокоды, рассылки, логи и настройки поверх `.env`.
- **Mini App** - личный кабинет, инструкции установки, [Telegram OAuth](features/telegram-auth.md), [вход по email](features/email-login.md) и публичные реферальные ссылки.
- **Операционные инструменты** - админка, тикеты поддержки, промокоды, рассылки, логи, [бэкапы и восстановление](features/backups.md), настройки поверх `.env`.
+1 -1
View File
@@ -92,7 +92,7 @@ docker compose logs -f backend
- `/auth/telegram/start` и `/auth/telegram/callback` проходят через frontend nginx в `backend:8081`;
- в браузере после callback нет статуса `telegram_auth=invalid_state`, `invalid_token`, `not_configured`, `unauthorized` или `failed`.
Подробности по маршрутам и настройке OAuth: [веб-приложение / Mini App](../features/web-app.md).
Подробности по маршрутам и настройке OAuth: [Telegram-авторизация](../features/telegram-auth.md).
## После изменения конфигурации
+14 -1
View File
@@ -1,6 +1,6 @@
# Обслуживание
Плановое обслуживание обычно сводится к обновлению образов, проверке миграций, логов и резервных копий PostgreSQL.
Плановое обслуживание обычно сводится к обновлению образов, проверке миграций, логов и резервных копий. Подробная инструкция по автоматическим ZIP-бэкапам и восстановлению вынесена в [бэкапы и восстановление](../features/backups.md).
## Обновление
@@ -16,6 +16,19 @@ docker compose logs -f migrate backend worker
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > backup.sql
```
## Автоматические бэкапы
Worker может собирать ZIP-архивы с дампом PostgreSQL и snapshot compose-папки, отправлять их в Telegram и хранить последние архивы в `data/backups`. Настройка и восстановление описаны в [отдельном разделе](../features/backups.md).
После изменения backup-настроек в `.env` перезапустите backend и worker:
```bash
docker compose up -d --build backend worker
docker compose logs -f backend worker
```
Восстановление из архива доступно в админке **Система -> Бэкапы**. Там же можно загрузить ZIP вручную, выбрать `БД` и/или `compose-папка`, а backend проверит архив перед запуском. Подробности: [бэкапы и восстановление](../features/backups.md#восстановление-из-админки).
## Проверки после работ
- `docker compose ps`