diff --git a/.dockerignore b/.dockerignore index ea5a02f..9f817d2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,7 +16,7 @@ frontend/node_modules/ docs-site/node_modules/ docs-site/.astro/ docs-site/dist/ -docs-site/src/content/docs/reference/ +docs-site/src/content/docs/ deploy/compose/docker-compose-dev.yml data/* !data/tariffs.example.json diff --git a/.gitignore b/.gitignore index 33ba041..3150f45 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ node_modules/ # Documentation site build artifacts docs-site/.astro/ docs-site/dist/ -docs-site/src/content/docs/reference/ +docs-site/src/content/docs/ # WebApp build artifacts (regenerated by `npm run build:webapp` / Docker build) bot/app/web/templates/subscription_webapp.css diff --git a/README.md b/README.md index 9fa59b8..1dfb662 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Remnawave Minishop - Telegram-бот и Web App (Mini App) для продажи и управления подписками панели [Remnawave](https://docs.rw/). Бот обрабатывает регистрацию, оплату, продление, пробный период, промокоды, рефералов и поддержку в чате. Web App показывает ссылку подключения, срок действия, трафик, оплату, устройства и вход по Telegram Mini Apps `initData`, Telegram OAuth / OpenID Connect и одноразовому email-коду. -Проект является переработанным форком [kavore/remnawave-tg-shop](https://github.com/kavore/remnawave-tg-shop). Для переноса данных из прежнего стека используйте [инструкцию по миграции](docs/migration-to-minishop.md). +Проект является переработанным форком [kavore/remnawave-tg-shop](https://github.com/kavore/remnawave-tg-shop). Для переноса данных из прежнего стека и других ботов используйте [раздел миграций](docs/migrations/index.md). ## Возможности @@ -32,15 +32,18 @@ Remnawave Minishop - Telegram-бот и Web App (Mini App) для продажи ## Документация +- [Входная страница документации](docs/index.md) - маршрут по установке, настройке, платежам, админке и диагностике. +- [Deploy examples](docs/deploy-examples/index.md) - готовые варианты запуска: Caddy, Nginx, Pangolin/Newt и no-proxy. - [Настройка окружения](docs/configuration.md) - bootstrap `.env` и рекомендуемая настройка через Web App админку. -- [Переменные `.env`](docs/env-vars.md) - полный справочник всех env-ключей по разделам. -- [Тарифы](docs/tariffs.md) - каталог тарифов, period- и traffic-модели, обычные и premium-докупки, premium-сквады, смена тарифа, HWID-лимиты и обработка трафика. -- [Админ-панель](docs/admin.md) - права доступа, настройки, редактор тарифов, premium-сквады и сохранение JSON-каталога. -- [Web App / Mini App](docs/webapp.md) - отдельный порт, домен, Telegram OAuth, email-вход, инструкции установки и реферальные ссылки. -- [Поддержка](docs/support.md) - тикеты в Mini App, входящий список админки, уведомления, лимиты и внешняя ссылка поддержки. -- [Темы Web App](docs/webapp-themes.md) - кастомные темы, настройка внешнего вида, логотипы, CSS/ассеты и пайплайн создания новой темы. +- [Переменные `.env`](docs/configuration/env-vars.md) - полный справочник всех env-ключей по разделам. +- [Тарифы](docs/features/tariffs.md) - каталог тарифов, period- и traffic-модели, обычные и premium-докупки, premium-сквады, смена тарифа, HWID-лимиты и обработка трафика. +- [Админ-панель](docs/features/admin-panel.md) - права доступа, настройки, редактор тарифов, premium-сквады и сохранение JSON-каталога. +- [Web App / Mini App](docs/features/web-app.md) - отдельный порт, домен, Telegram OAuth, email-вход, инструкции установки и реферальные ссылки. +- [Поддержка](docs/features/support.md) - тикеты в Mini App, входящий список админки, уведомления, лимиты и внешняя ссылка поддержки. +- [Темы Web App](docs/features/webapp-themes.md) - кастомные темы, настройка внешнего вида, логотипы, CSS/ассеты и пайплайн создания новой темы. - [Развертывание](docs/deployment.md) - Docker Compose, reverse proxy, Nginx, Caddy, вебхуки, запуск из образа и обновление версии (`IMAGE_TAG`). -- [Миграция с remnawave-tg-shop](docs/migration-to-minishop.md) - перенос данных из прежнего стека. +- [Миграции](docs/migrations/index.md) - готовые сценарии переноса с других ботов; сейчас описан `remnawave-tg-shop`. +- [Миграция с remnawave-tg-shop](docs/migrations/remnawave-tg-shop.md) - готовый сценарий для legacy-стека. ## Совместимость @@ -88,9 +91,9 @@ docker compose logs -f backend worker frontend - `PANEL_API_URL`, `PANEL_API_KEY`, `PANEL_WEBHOOK_SECRET` - доступ к Remnawave; - остальные настройки удобнее задать в Web App админке. -После первого входа в админку настройте тарифы, платежные провайдеры, внешний вид, поддержку, уведомления и инструкции подключения через UI. Инструкции установки включены по умолчанию, читают Subscription Page config из Remnawave Panel и при проблемах с конфигом откатываются к обычной ссылке подключения. Полный справочник env-переменных: [docs/env-vars.md](docs/env-vars.md). +После первого входа в админку настройте тарифы, платежные провайдеры, внешний вид, поддержку, уведомления и инструкции подключения через UI. Инструкции установки включены по умолчанию, читают Subscription Page config из Remnawave Panel и при проблемах с конфигом откатываются к обычной ссылке подключения. Полный справочник env-переменных: [docs/configuration/env-vars.md](docs/configuration/env-vars.md). -Для каталога тарифов используется `TARIFFS_CONFIG_PATH` со значением по умолчанию `data/tariffs.json`. Пример формата лежит в [data/tariffs.example.json](data/tariffs.example.json), подробности - в [docs/tariffs.md](docs/tariffs.md). +Для каталога тарифов используется `TARIFFS_CONFIG_PATH` со значением по умолчанию `data/tariffs.json`. Пример формата лежит в [data/tariffs.example.json](data/tariffs.example.json), подробности - в [docs/features/tariffs.md](docs/features/tariffs.md). Если в Docker Compose включаете bind mount `./data:/app/data`, заранее создайте каталог и отдайте его пользователю контейнера. Это нужно для сохранения `data/tariffs.json`, каталога тем `data/themes`, кеша логотипа Web App и animated emoji: @@ -120,7 +123,7 @@ docker compose up -d IMAGE_TAG=3.1.0 docker compose up -d ``` -Для production-запуска удобнее брать готовые папки из [`deploy/examples`](deploy/examples): там отдельно собраны варианты для Caddy, Nginx, Newt/Pangolin и прямой публикации портов без reverse proxy. В каждой папке рядом лежат `docker-compose.yml`, `.env.example`, README и нужный proxy-конфиг. +Для production-запуска удобнее брать готовые папки из [`deploy/examples`](deploy/examples), а читать каноничные инструкции в [docs/deploy-examples/index.md](docs/deploy-examples/index.md): там отдельно описаны варианты для Caddy, Nginx, Newt/Pangolin и прямой публикации портов без reverse proxy. В папках рядом с compose лежат только конфиги и короткие ссылки на документацию. GHCR image names for releases: diff --git a/deploy/examples/README.md b/deploy/examples/README.md index f79ea0a..67870a6 100644 --- a/deploy/examples/README.md +++ b/deploy/examples/README.md @@ -1,33 +1,12 @@ -# Готовые варианты запуска +# Deploy examples -В этой папке лежат самодостаточные compose-примеры. Каждый вариант запускается из своей директории обычной командой: +Каноничная документация по вариантам запуска живет в [docs/deploy-examples/index.md](../../docs/deploy-examples/index.md). -```bash -cp .env.example .env -nano .env -docker compose up -d -``` - -После старта полезно проверить: - -```bash -docker compose ps -docker compose logs -f backend worker frontend -``` - -## Какой вариант выбрать - -| Папка | Когда использовать | Что править | -| --- | --- | --- | -| [`caddy`](caddy) | Нужен самый простой публичный HTTPS с автоматическими сертификатами Let's Encrypt. | `.env`; при нестандартной схеме можно поправить `Caddyfile`. | -| [`nginx`](nginx) | Уже используете Nginx и готовы положить TLS-сертификаты рядом с примером. | `.env`, `nginx.conf.template`, файлы в `ssl/`. | -| [`newt`](newt) | Публикуете сервисы через Pangolin/Newt без входящих портов на сервере приложения. | `.env` и ресурсы в панели Pangolin. | -| [`no-proxy`](no-proxy) | Нужно напрямую открыть порты backend/frontend или проверить стек без reverse proxy. | `.env`. | - -Для всех вариантов нужны два публичных URL: - -- webhook/backend URL для Telegram, платежных систем и Remnawave webhooks; -- Mini App/frontend URL для Telegram Mini App и Web App. - -Обычно это два домена, например `webhooks.example.com` и `app.example.com`. +Эта папка хранит только рабочие compose-примеры и конфиги. Подробное описание не дублируется здесь, чтобы сайт документации и навигация из README использовали один источник. +| Папка | Документация | +| --- | --- | +| `caddy` | [Caddy](../../docs/deploy-examples/caddy.md) | +| `nginx` | [Nginx](../../docs/deploy-examples/nginx.md) | +| `newt` | [Pangolin / Newt](../../docs/deploy-examples/newt.md) | +| `no-proxy` | [No proxy](../../docs/deploy-examples/no-proxy.md) | diff --git a/deploy/examples/caddy/README.md b/deploy/examples/caddy/README.md index e0db906..20686f2 100644 --- a/deploy/examples/caddy/README.md +++ b/deploy/examples/caddy/README.md @@ -1,31 +1,5 @@ -# Запуск с Caddy +# Caddy -Caddy сам выпускает и продлевает HTTPS-сертификаты. На сервере должны быть открыты входящие `80/tcp` и `443/tcp`, а DNS-записи `WEBHOOK_HOST` и `MINIAPP_HOST` должны смотреть на этот сервер. - -```bash -cp .env.example .env -nano .env -docker compose up -d -``` - -Минимально поменяйте в `.env`: - -- `WEBHOOK_HOST` и `MINIAPP_HOST`; -- `BOT_TOKEN`, `ADMIN_IDS`; -- `POSTGRES_PASSWORD`; -- `WEBAPP_SESSION_SECRET`, `WEBHOOK_SECRET_TOKEN`; -- `PANEL_API_URL`, `PANEL_API_KEY`, `PANEL_WEBHOOK_SECRET`. - -`Caddyfile` лежит рядом и использует домены из `.env`. Если нужна нестандартная логика Caddy, правьте его и перезапускайте: - -```bash -docker compose up -d --force-recreate caddy -``` - -Проверка: - -```bash -docker compose ps -docker compose logs -f caddy backend worker frontend -``` +Каноничная инструкция: [docs/deploy-examples/caddy.md](../../../docs/deploy-examples/caddy.md). +Файлы этого примера остаются рядом: `docker-compose.yml`, `.env.example` и `Caddyfile`. diff --git a/deploy/examples/newt/README.md b/deploy/examples/newt/README.md index 65a8c18..ef3cd4e 100644 --- a/deploy/examples/newt/README.md +++ b/deploy/examples/newt/README.md @@ -1,33 +1,5 @@ -# Запуск через Newt / Pangolin +# Pangolin / Newt -Этот вариант не открывает входящие порты на сервере приложения. Newt подключается к Pangolin, а публичные домены настраиваются ресурсами в панели Pangolin. +Каноничная инструкция: [docs/deploy-examples/newt.md](../../../docs/deploy-examples/newt.md). -```bash -cp .env.example .env -nano .env -docker compose up -d -``` - -В `.env` заполните: - -- `WEBHOOK_HOST` и `MINIAPP_HOST` - публичные домены ресурсов в Pangolin; -- `PANGOLIN_ENDPOINT`, `NEWT_ID`, `NEWT_SECRET` - значения из настроек site/client в Pangolin; -- обычные переменные приложения: `BOT_TOKEN`, `ADMIN_IDS`, `POSTGRES_PASSWORD`, секреты и доступ к Remnawave. - -Официальная инструкция Pangolin по установке Newt site: . - -В Pangolin создайте два HTTP-ресурса для этого Newt site: - -| Публичный домен | Upstream | -| --- | --- | -| `https://webhooks.example.com` | `http://backend:8080` | -| `https://app.example.com` | `http://frontend:80` | - -Домены в Pangolin должны совпадать с `WEBHOOK_HOST` и `MINIAPP_HOST`. - -Проверка: - -```bash -docker compose ps -docker compose logs -f newt backend worker frontend -``` +Файлы этого примера остаются рядом: `docker-compose.yml` и `.env.example`. diff --git a/deploy/examples/nginx/README.md b/deploy/examples/nginx/README.md index c1b1c22..675b9de 100644 --- a/deploy/examples/nginx/README.md +++ b/deploy/examples/nginx/README.md @@ -1,42 +1,5 @@ -# Запуск с Nginx +# Nginx -Этот пример поднимает Nginx в той же Docker-сети, что и приложение: - -- `WEBHOOK_HOST` проксируется в `backend:8080`; -- `MINIAPP_HOST` проксируется в `frontend:80`; -- `frontend` сам проксирует внутренние `/api`, `/auth` и ассеты тем в `backend:8081`. - -## Подготовка - -```bash -cp .env.example .env -nano .env -``` - -Положите TLS-сертификаты в `ssl/`: - -```text -ssl/ - webhooks.example.com/ - fullchain.pem - privkey.pem - app.example.com/ - fullchain.pem - privkey.pem -``` - -Имена папок должны совпадать с `WEBHOOK_HOST` и `MINIAPP_HOST` в `.env`. - -## Запуск - -```bash -docker compose up -d -docker compose logs -f nginx backend worker frontend -``` - -Если нужно поменять заголовки, лимиты или TLS-настройки, правьте `nginx.conf.template` рядом с compose и перезапускайте Nginx: - -```bash -docker compose up -d --force-recreate nginx -``` +Каноничная инструкция: [docs/deploy-examples/nginx.md](../../../docs/deploy-examples/nginx.md). +Файлы этого примера остаются рядом: `docker-compose.yml`, `.env.example`, `nginx.conf.template` и папка `ssl/` для сертификатов. diff --git a/deploy/examples/nginx/ssl/README.md b/deploy/examples/nginx/ssl/README.md index 55c952f..f1c4eeb 100644 --- a/deploy/examples/nginx/ssl/README.md +++ b/deploy/examples/nginx/ssl/README.md @@ -1,8 +1,8 @@ -# TLS-сертификаты для Nginx +# TLS certificates -Положите сюда сертификаты для доменов из `.env`. +Каноничная инструкция по Nginx: [docs/deploy-examples/nginx.md](../../../../docs/deploy-examples/nginx.md). -Пример структуры: +Кладите сертификаты в подпапки, совпадающие с `WEBHOOK_HOST` и `MINIAPP_HOST`: ```text ssl/ @@ -13,6 +13,3 @@ ssl/ fullchain.pem privkey.pem ``` - -Если используете wildcard-сертификат, можно положить одинаковые `fullchain.pem` и `privkey.pem` в обе папки. - diff --git a/deploy/examples/no-proxy/README.md b/deploy/examples/no-proxy/README.md index bd45498..2e2a6b1 100644 --- a/deploy/examples/no-proxy/README.md +++ b/deploy/examples/no-proxy/README.md @@ -1,23 +1,5 @@ -# Запуск без reverse proxy +# No proxy -Этот вариант напрямую публикует два HTTP-порта: - -- backend/webhooks: `WEB_SERVER_BIND`, по умолчанию `0.0.0.0:8080`; -- frontend/Mini App: `FRONTEND_BIND`, по умолчанию `0.0.0.0:8082`. - -```bash -cp .env.example .env -nano .env -docker compose up -d -``` - -Важно: контейнеры приложения сами не выпускают TLS-сертификаты. Для реального Telegram webhook и Mini App публичные URL должны быть HTTPS. Используйте этот вариант для локальной проверки, внутренней сети или когда HTTPS уже завершается внешней платформой и дальше трафик приходит на эти порты. - -Проверка локально: - -```bash -curl http://127.0.0.1:8080/healthz -curl http://127.0.0.1:8082/health -docker compose logs -f backend worker frontend -``` +Каноничная инструкция: [docs/deploy-examples/no-proxy.md](../../../docs/deploy-examples/no-proxy.md). +Файлы этого примера остаются рядом: `docker-compose.yml` и `.env.example`. diff --git a/docs-site/DEPLOY.md b/docs-site/DEPLOY.md deleted file mode 100644 index 74a9f0f..0000000 --- a/docs-site/DEPLOY.md +++ /dev/null @@ -1,45 +0,0 @@ -# Cloudflare Pages deploy - -This docs site is built with Astro Starlight and publishes to: - -```text -https://minishop.minidoc.cc -``` - -## Cloudflare Pages settings - -Create a Pages project connected to the GitLab repository and use: - -| Setting | Value | -| --- | --- | -| Production branch | `main` | -| Framework preset | `Astro` | -| Root directory | `docs-site` | -| Build command | `npm ci && npm run build` | -| Build output directory | `dist` | -| Node version | `22` | - -The build script runs `scripts/sync-docs.mjs` before Astro builds the site. Keep editing the canonical Markdown files in the repository-level `docs/` directory. - -## Custom domain - -After the first successful Pages deploy: - -1. Open the Pages project in Cloudflare. -2. Go to **Custom domains**. -3. Add `minishop.minidoc.cc`. -4. If `minidoc.cc` is already on Cloudflare DNS, accept the suggested DNS record and wait for TLS activation. - -## Optional API automation - -Do not use a root token for automation. Create a scoped Cloudflare API token and expose it locally as an environment variable only for the setup command. - -Minimum useful permissions: - -| Scope | Permission | -| --- | --- | -| Account | Cloudflare Pages: Edit | -| Zone: `minidoc.cc` | Zone: Read | -| Zone: `minidoc.cc` | DNS: Edit | - -Cloudflare's GitLab integration still requires the Cloudflare GitLab app/OAuth connection to be authorized for the repository. If that is not connected yet, complete the GitLab connection in the Cloudflare dashboard first, or use a direct-upload Pages workflow instead of Git-connected deployments. diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index cccb453..0dd3ddc 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -6,23 +6,20 @@ export default defineConfig({ site: 'https://minishop.minidoc.cc', integrations: [ starlight({ - title: 'Remnawave Minishop', + title: 'minishop', + favicon: '/favicon.png', description: 'Документация по настройке, развертыванию и эксплуатации Remnawave Minishop.', plugins: [ starlightThemeNova({ nav: [ - { label: 'Запуск', href: '/reference/deployment/' }, - { label: 'Настройка', href: '/reference/configuration/' }, + { label: 'Главная', href: '/' }, + { label: 'Установка', href: '/getting-started/setup/' }, + { label: 'Платежи', href: '/features/payments/' }, { label: 'GitLab', href: 'https://gitlab.com/3252a8/remnawave-minshop' }, ], }), ], - favicon: '/favicon.svg', - logo: { - src: './src/assets/logo.svg', - alt: 'Remnawave Minishop', - }, customCss: ['./src/styles/custom.css'], lastUpdated: false, locales: { @@ -32,11 +29,19 @@ export default defineConfig({ }, }, head: [ + { + tag: 'link', + attrs: { + rel: 'icon', + href: '/favicon.webp', + type: 'image/webp', + }, + }, { tag: 'meta', attrs: { name: 'theme-color', - content: '#0f766e', + content: '#00fe7a', }, }, { @@ -49,32 +54,83 @@ export default defineConfig({ ], sidebar: [ { - label: 'Обзор', - link: '/', + label: 'Начало', + items: [ + { label: 'Главная', link: '/' }, + { label: 'Обзор', slug: 'getting-started/overview' }, + { label: 'Установка', slug: 'getting-started/setup' }, + { label: 'Deploy examples', slug: 'deploy-examples' }, + ], }, { - label: 'Запуск', + label: 'Варианты деплоя', items: [ + { label: 'Caddy', slug: 'deploy-examples/caddy' }, + { label: 'Nginx', slug: 'deploy-examples/nginx' }, + { label: 'Pangolin / Newt', slug: 'deploy-examples/newt' }, + { label: 'Без reverse proxy', slug: 'deploy-examples/no-proxy' }, + ], + }, + { + label: 'Конфигурация', + items: [ + { label: 'Переменные', slug: 'configuration/env-vars' }, { label: 'Настройка окружения', slug: 'reference/configuration' }, - { label: 'Переменные .env', slug: 'reference/env-vars' }, - { label: 'Развертывание', slug: 'reference/deployment' }, - { label: 'Миграция', slug: 'reference/migration-to-minishop' }, + { label: 'Безопасность', slug: 'configuration/security' }, ], }, { - label: 'Web App', + label: 'Возможности', items: [ - { label: 'Mini App', slug: 'reference/webapp' }, - { label: 'Темы и внешний вид', slug: 'reference/webapp-themes' }, - { label: 'Админ-панель', slug: 'reference/admin' }, - { label: 'Поддержка', slug: 'reference/support' }, + { label: 'Основные', slug: 'features/core' }, + { label: 'Платежи', slug: 'features/payments' }, + { label: 'Подписки', slug: 'features/subscriptions' }, + { label: 'Тарифы', slug: 'features/tariffs' }, + { label: 'Mini App', slug: 'features/web-app' }, + { label: 'Темы Web App', slug: 'features/webapp-themes' }, + { label: 'Админ-панель', slug: 'features/admin-panel' }, + { label: 'Поддержка', slug: 'features/support' }, ], }, { - label: 'Продукт', + label: 'Платежные системы', + items: [ + { label: 'YooKassa', slug: 'payments/yookassa' }, + { label: 'FreeKassa', slug: 'payments/freekassa' }, + { label: 'Platega', slug: 'payments/platega' }, + { label: 'SeverPay', slug: 'payments/severpay' }, + { label: 'Wata', slug: 'payments/wata' }, + { label: 'CryptoPay', slug: 'payments/cryptopay' }, + { label: 'Heleket', slug: 'payments/heleket' }, + { label: 'Telegram Stars', slug: 'payments/telegram-stars' }, + ], + }, + { + label: 'Администрирование', + items: [ + { label: 'Пользователи', slug: 'administration/users' }, + { label: 'Обслуживание', slug: 'administration/maintenance' }, + ], + }, + { + label: 'Миграции', + items: [ + { label: 'Обзор миграций', slug: 'migrations' }, + { label: 'remnawave-tg-shop', slug: 'migrations/remnawave-tg-shop' }, + ], + }, + { + label: 'Устранение неполадок', + items: [ + { label: 'Проблемы', slug: 'troubleshooting/issues' }, + { label: 'Логи', slug: 'troubleshooting/logs' }, + ], + }, + { + label: 'Справочник', items: [ - { label: 'Тарифы', slug: 'reference/tariffs' }, { label: 'Архитектура', slug: 'reference/architecture' }, + { label: 'Развертывание', slug: 'reference/deployment' }, ], }, ], diff --git a/docs-site/public/favicon.png b/docs-site/public/favicon.png new file mode 100644 index 0000000..387b954 Binary files /dev/null and b/docs-site/public/favicon.png differ diff --git a/docs-site/public/favicon.svg b/docs-site/public/favicon.svg deleted file mode 100644 index 15401b8..0000000 --- a/docs-site/public/favicon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs-site/public/favicon.webp b/docs-site/public/favicon.webp new file mode 100644 index 0000000..11b0cb3 Binary files /dev/null and b/docs-site/public/favicon.webp differ diff --git a/docs-site/scripts/sync-docs.mjs b/docs-site/scripts/sync-docs.mjs index f3d663d..b91ae97 100644 --- a/docs-site/scripts/sync-docs.mjs +++ b/docs-site/scripts/sync-docs.mjs @@ -5,19 +5,44 @@ import { fileURLToPath } from 'node:url'; const siteRoot = path.resolve(fileURLToPath(new URL('..', import.meta.url))); const repoRoot = path.resolve(siteRoot, '..'); const sourceDir = path.join(repoRoot, 'docs'); -const outputDir = path.join(siteRoot, 'src', 'content', 'docs', 'reference'); +const outputDir = path.join(siteRoot, 'src', 'content', 'docs'); const descriptions = { - 'admin.md': 'Возможности админ-панели, управление пользователями, настройками, тарифами и поддержкой.', + 'index.md': 'Документация по запуску, настройке и сопровождению Telegram Mini App для Remnawave.', + 'getting-started/overview.md': 'Что входит в Remnawave Minishop и как связаны бот, Mini App, backend, worker и Remnawave Panel.', + 'getting-started/setup.md': 'Минимальный путь запуска Remnawave Minishop через Docker Compose.', + 'configuration/security.md': 'Секреты, публичные URL, доступ администраторов и базовые меры защиты Minishop.', + 'configuration/env-vars.md': 'Полный справочник переменных окружения Remnawave Minishop.', + 'features/core.md': 'Пользовательские и админские сценарии Remnawave Minishop.', + 'features/payments.md': 'Платежные провайдеры, кнопки оплаты и webhook-обработка.', + 'features/subscriptions.md': 'Period- и traffic-тарифы, premium-сквады, HWID-устройства и жизненный цикл подписки.', + 'features/tariffs.md': 'Каталог тарифов, period/traffic-модели, premium-сквады и HWID-устройства.', + 'features/web-app.md': 'Telegram Mini App, авторизация, публичные инструкции и проксирование.', + 'features/webapp-themes.md': 'Кастомные темы, CSS-токены, ассеты и пайплайн создания темы.', + 'features/admin-panel.md': 'Возможности админ-панели, управление пользователями, настройками, тарифами и поддержкой.', + 'features/support.md': 'Пользовательские тикеты, админский inbox, уведомления и лимиты поддержки.', + 'deploy-examples/index.md': 'Как выбрать готовый deploy example для production или проверки.', + 'deploy-examples/caddy.md': 'Запуск Remnawave Minishop с Caddy и автоматическим HTTPS.', + 'deploy-examples/nginx.md': 'Запуск Remnawave Minishop с Nginx и внешними TLS-сертификатами.', + 'deploy-examples/newt.md': 'Запуск через Pangolin/Newt без входящих портов на сервере приложения.', + 'deploy-examples/no-proxy.md': 'Прямой запуск backend и frontend портов без reverse proxy.', + 'migrations/index.md': 'Готовые сценарии миграции в Remnawave Minishop с других ботов.', + 'migrations/remnawave-tg-shop.md': 'Перенос данных со старого remnawave-tg-shop на split-архитектуру Minishop.', + 'payments/yookassa.md': 'Быстрый вход в настройку YooKassa для Remnawave Minishop.', + 'payments/freekassa.md': 'Быстрый вход в настройку FreeKassa для Remnawave Minishop.', + 'payments/platega.md': 'Настройка Platega, отдельных СБП/карта и crypto-кнопок.', + 'payments/severpay.md': 'Настройка SeverPay, MID, token, return URL и срока жизни ссылки.', + 'payments/wata.md': 'Настройка Wata, API token, TTL ссылки, подписи webhook и trusted IP.', + 'payments/cryptopay.md': 'Настройка CryptoPay, токена, сети, currency type и asset.', + 'payments/heleket.md': 'Настройка Heleket, merchant ID, payment API key, invoice currency и webhook-проверок.', + 'payments/telegram-stars.md': 'Оплата подписок и докупок через Telegram Stars.', + 'administration/users.md': 'Где управлять пользователями, подписками, блокировками и поддержкой.', + 'administration/maintenance.md': 'Обновления, миграции, резервные копии и проверки production-стека.', + 'troubleshooting/issues.md': 'Короткие чеклисты для частых проблем запуска, вебхуков, Mini App и платежей.', + 'troubleshooting/logs.md': 'Какие логи смотреть при диагностике backend, worker, frontend, миграций и вебхуков.', 'architecture.md': 'Краткая архитектура backend, frontend, worker и инфраструктурных сервисов.', 'configuration.md': 'Минимальный .env, bootstrap-секреты и настройка через Web App админку.', 'deployment.md': 'Docker Compose, reverse proxy, TLS, образы, обновления и резервные копии.', - 'env-vars.md': 'Полный справочник переменных окружения Remnawave Minishop.', - 'migration-to-minishop.md': 'Перенос данных со старого remnawave-tg-shop на split-архитектуру Minishop.', - 'support.md': 'Пользовательские тикеты, админский inbox, уведомления и лимиты поддержки.', - 'tariffs.md': 'Каталог тарифов, period/traffic-модели, premium-сквады и HWID-устройства.', - 'webapp.md': 'Telegram Mini App, авторизация, публичные инструкции и проксирование.', - 'webapp-themes.md': 'Кастомные темы, CSS-токены, ассеты и пайплайн создания темы.', }; const imageExtensions = new Set(['.avif', '.gif', '.jpeg', '.jpg', '.png', '.svg', '.webp']); @@ -26,23 +51,45 @@ function yamlString(value) { return JSON.stringify(value); } -function slugFor(fileName) { - return fileName.replace(/\.md$/i, ''); +function toPosix(relativePath) { + return relativePath.split(path.sep).join('/'); } -function extractTitle(fileName, content) { +function outputRelativePath(sourceRelativePath) { + if (sourceRelativePath === 'index.md') { + return 'index.md'; + } + if (!sourceRelativePath.includes('/')) { + return `reference/${sourceRelativePath}`; + } + return sourceRelativePath; +} + +function pagePathForSource(sourceRelativePath, hash = '') { + const output = outputRelativePath(sourceRelativePath).replace(/\.md$/i, ''); + const route = output === 'index' ? '/' : `/${output.replace(/\/index$/u, '')}/`; + return `${route}${hash}`; +} + +function titleForRelativePath(relativePath) { + const baseName = path.posix.basename(relativePath, '.md'); + return baseName; +} + +function extractTitle(relativePath, content) { const match = content.match(/^#\s+(.+?)\s*$/m); - return match?.[1] ?? slugFor(fileName); + return match?.[1] ?? titleForRelativePath(relativePath); } function stripFirstHeading(content) { return content.replace(/^#\s+.+?\s*\r?\n+/, ''); } -function rewriteMarkdownLinks(markdown) { +function rewriteMarkdownLinks(markdown, sourceRelativePath) { + const sourceDirectory = path.posix.dirname(sourceRelativePath); return markdown.replace(/\]\((?!https?:\/\/|mailto:|tel:|\/|#)([^)\s]+\.md)(#[^)]+)?\)/g, (match, target, hash = '') => { - const slug = slugFor(path.posix.basename(target)); - return `](/reference/${slug}/${hash})`; + const resolvedTarget = path.posix.normalize(path.posix.join(sourceDirectory, target)); + return `](${pagePathForSource(resolvedTarget, hash)})`; }); } @@ -52,45 +99,105 @@ function normalizeCodeFences(markdown) { .replace(/^```caddyfile\s*$/gim, '```txt'); } -function frontmatter({ title, description, fileName }) { - const editUrl = `https://gitlab.com/3252a8/remnawave-minshop/-/edit/main/docs/${encodeURIComponent(fileName)}`; +function extraFrontmatter(sourceRelativePath) { + if (sourceRelativePath !== 'index.md') { + return []; + } + + return [ + 'template: splash', + 'hero:', + ' tagline: "Telegram-бот и Mini App для продажи подписок Remnawave: платежи, тарифы, админка, поддержка и инструкции подключения."', + ' image:', + ' html: \'Интерфейс Remnawave Minishop\'', + ' actions:', + ' - text: "Быстрый старт"', + ' link: /getting-started/setup/', + ' icon: right-arrow', + ' - text: "Deploy examples"', + ' link: /deploy-examples/', + ' icon: setting', + ' variant: minimal', + ]; +} + +function frontmatter({ title, description, sourceRelativePath }) { + const editPath = sourceRelativePath + .split('/') + .map((segment) => encodeURIComponent(segment)) + .join('/'); + const editUrl = `https://gitlab.com/3252a8/remnawave-minshop/-/edit/main/docs/${editPath}`; return [ '---', `title: ${yamlString(title)}`, `description: ${yamlString(description)}`, `editUrl: ${yamlString(editUrl)}`, + ...extraFrontmatter(sourceRelativePath), '---', '', ].join('\n'); } -async function syncMarkdown(entries) { - for (const entry of entries.filter((item) => item.name.endsWith('.md'))) { - const sourcePath = path.join(sourceDir, entry.name); +async function walk(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const absolutePath = path.join(directory, entry.name); + if (entry.isDirectory()) { + files.push(...(await walk(absolutePath))); + continue; + } + if (entry.isFile()) { + files.push(absolutePath); + } + } + return files; +} + +async function syncMarkdown(files) { + for (const sourcePath of files.filter((file) => file.endsWith('.md'))) { + const sourceRelativePath = toPosix(path.relative(sourceDir, sourcePath)); + const outputRelative = outputRelativePath(sourceRelativePath); + const outputPath = path.join(outputDir, ...outputRelative.split('/')); const content = await readFile(sourcePath, 'utf8'); - const title = extractTitle(entry.name, content); - const body = normalizeCodeFences(rewriteMarkdownLinks(stripFirstHeading(content).trimStart())); + const title = extractTitle(sourceRelativePath, content); + const body = normalizeCodeFences( + rewriteMarkdownLinks(stripFirstHeading(content).trimStart(), sourceRelativePath), + ); const output = frontmatter({ title, - description: descriptions[entry.name] ?? title, - fileName: entry.name, + description: descriptions[sourceRelativePath] ?? title, + sourceRelativePath, }); - await writeFile(path.join(outputDir, entry.name), `${output}${body}\n`, 'utf8'); + await mkdir(path.dirname(outputPath), { recursive: true }); + await writeFile(outputPath, `${output}${body}\n`, 'utf8'); } } -async function syncImages(entries) { - for (const entry of entries.filter((item) => imageExtensions.has(path.extname(item.name).toLowerCase()))) { - await copyFile(path.join(sourceDir, entry.name), path.join(outputDir, entry.name)); +async function syncAssets(files) { + for (const sourcePath of files.filter((file) => imageExtensions.has(path.extname(file).toLowerCase()))) { + const sourceRelativePath = toPosix(path.relative(sourceDir, sourcePath)); + const outputRelative = !sourceRelativePath.includes('/') + ? sourceRelativePath + : sourceRelativePath; + const outputPath = path.join(outputDir, ...outputRelative.split('/')); + await mkdir(path.dirname(outputPath), { recursive: true }); + await copyFile(sourcePath, outputPath); + + if (!sourceRelativePath.includes('/')) { + const referenceOutputPath = path.join(outputDir, 'reference', sourceRelativePath); + await mkdir(path.dirname(referenceOutputPath), { recursive: true }); + await copyFile(sourcePath, referenceOutputPath); + } } } await rm(outputDir, { recursive: true, force: true }); await mkdir(outputDir, { recursive: true }); -const entries = await readdir(sourceDir, { withFileTypes: true }); -await syncMarkdown(entries); -await syncImages(entries); +const files = await walk(sourceDir); +await syncMarkdown(files); +await syncAssets(files); console.log(`Synced documentation from ${path.relative(repoRoot, sourceDir)} to ${path.relative(repoRoot, outputDir)}`); diff --git a/docs-site/src/assets/logo.svg b/docs-site/src/assets/logo.svg deleted file mode 100644 index cfd8eb4..0000000 --- a/docs-site/src/assets/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - Remnawave Minishop - Abstract layered wave mark. - - - - diff --git a/docs-site/src/content/docs/index.md b/docs-site/src/content/docs/index.md deleted file mode 100644 index 6874bac..0000000 --- a/docs-site/src/content/docs/index.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Remnawave Minishop" -description: "Документация по запуску, настройке и сопровождению Telegram Mini App для Remnawave." -template: splash -hero: - tagline: "Telegram-бот и Mini App для продажи подписок Remnawave: платежи, тарифы, админка, поддержка и инструкции подключения." - image: - alt: Интерфейс Remnawave Minishop - html: 'Интерфейс Remnawave Minishop' - actions: - - text: Быстрый старт - link: /reference/deployment/ - icon: right-arrow - - text: Настройка - link: /reference/configuration/ - icon: setting - variant: minimal ---- - -## Основные разделы - -- **Запуск и окружение** - минимальный `.env`, Docker Compose, reverse proxy, обновления и резервные копии. -- **Web App / Mini App** - Telegram-авторизация, email-вход, инструкции установки и публичные ссылки. -- **Админка и тарифы** - управление пользователями, платежами, темами, каталогом тарифов и premium-сквадами. -- **Миграция** - перенос со старого `remnawave-tg-shop` на текущую split-архитектуру. - -## Быстрые ссылки - -- [Развертывание](/reference/deployment/) -- [Переменные окружения](/reference/env-vars/) -- [Тарифы](/reference/tariffs/) -- [Темы Web App](/reference/webapp-themes/) diff --git a/docs-site/src/styles/custom.css b/docs-site/src/styles/custom.css index 71fdc73..ce05293 100644 --- a/docs-site/src/styles/custom.css +++ b/docs-site/src/styles/custom.css @@ -1,25 +1,73 @@ @layer starlight, nova, minishop; @layer minishop { - .site-title { + .site-title, + .site-title span { + color: #00fe7a; font-weight: 760; } + .site-title:hover, + .site-title:hover span { + color: #00fe7a; + } + .hero { gap: clamp(2rem, 6vw, 5rem); padding-block: clamp(3.5rem, 10vw, 6.5rem); } + .hero > .hero-html:has(.minishop-hero-screenshot) { + width: min(96vw, 72rem); + max-width: none; + } + .hero img { border: 1px solid var(--sl-color-gray-5); border-radius: 10px; box-shadow: 0 24px 70px rgb(15 23 42 / 18%); } + .minishop-hero-screenshot { + display: block; + width: 100%; + max-width: none; + height: auto; + } + :root[data-theme='dark'] .hero img { box-shadow: 0 24px 70px rgb(0 0 0 / 36%); } + @media (min-width: 50rem) { + .hero:has(.minishop-hero-screenshot) { + grid-template-columns: minmax(0, 1fr); + gap: clamp(2rem, 4vw, 4rem); + align-items: center; + } + + .hero > .hero-html:has(.minishop-hero-screenshot) { + order: 2; + width: min(100%, 90rem); + margin-inline: auto; + } + + .hero:has(.minishop-hero-screenshot) .stack { + align-items: center; + max-width: 64rem; + margin-inline: auto; + text-align: center; + } + + .hero:has(.minishop-hero-screenshot) .copy { + align-items: center; + } + + .hero:has(.minishop-hero-screenshot) .actions { + justify-content: center; + } + } + .sl-markdown-content :is(h2, h3) { letter-spacing: 0; } diff --git a/docs/administration/maintenance.md b/docs/administration/maintenance.md new file mode 100644 index 0000000..0340eab --- /dev/null +++ b/docs/administration/maintenance.md @@ -0,0 +1,27 @@ +# Обслуживание + +Плановое обслуживание обычно сводится к обновлению образов, проверке миграций, логов и резервных копий PostgreSQL. + +## Обновление + +```bash +docker compose pull +docker compose up -d +docker compose logs -f migrate backend worker +``` + +## Резервная копия PostgreSQL + +```bash +docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > backup.sql +``` + +## Проверки после работ + +- `docker compose ps` +- `docker compose logs -f backend worker frontend` +- `/healthz` на backend-домене +- вход в Mini App и админку +- тестовый платеж или тестовая активация + +Подробности: [развертывание](../deployment.md) и [логи](../troubleshooting/logs.md). diff --git a/docs/administration/users.md b/docs/administration/users.md new file mode 100644 index 0000000..7b322c2 --- /dev/null +++ b/docs/administration/users.md @@ -0,0 +1,19 @@ +# Пользователи + +Пользовательские операции выполняются в Web App админке. Доступ получают только Telegram-пользователи из `ADMIN_IDS`. + +## Что доступно администратору + +- список пользователей с поиском и фильтрами; +- просмотр подписки, статуса, трафика и premium-лимитов; +- блокировка пользователя; +- ручная синхронизация с Remnawave Panel; +- тикеты поддержки и ответы пользователю; +- просмотр платежей и служебных событий. + +## Связанные разделы + +- [Админ-панель](../features/admin-panel.md) +- [Поддержка](../features/support.md) +- [Тарифы](../features/tariffs.md) +- [Mini App](../features/web-app.md) diff --git a/docs/configuration.md b/docs/configuration.md index 72bc26f..1ecffe5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,7 +7,7 @@ Админка сохраняет overrides в базе данных и применяет их поверх `.env`. Это удобно для платежей, внешнего вида, поддержки, уведомлений, legacy-цен и большинства пользовательских параметров. Тарифы редактируются отдельно в разделе **Система -> Тарифы** и сохраняются в JSON-файл `TARIFFS_CONFIG_PATH`. -Полный справочник всех переменных вынесен в [env-vars.md](env-vars.md). +Полный справочник всех переменных вынесен в [configuration/env-vars.md](configuration/env-vars.md). ## Минимальный `.env` @@ -102,9 +102,9 @@ docker compose exec backend sh -lc 'id; touch /app/data/themes/test && rm /app/d ## Дополнительные разделы -- [env-vars.md](env-vars.md) - полный справочник переменных `.env`. -- [admin.md](admin.md) - как устроены overrides и allowlist настроек. -- [tariffs.md](tariffs.md) - JSON-каталог тарифов и редактор тарифов. -- [webapp.md](webapp.md) - домен Mini App, Telegram OAuth и email-вход. -- [support.md](support.md) - тикеты поддержки и уведомления. +- [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-каталог тарифов и редактор тарифов. +- [features/web-app.md](features/web-app.md) - домен Mini App, Telegram OAuth и email-вход. +- [features/support.md](features/support.md) - тикеты поддержки и уведомления. - [deployment.md](deployment.md) - Docker Compose, reverse proxy, Caddy/Nginx и обновления. diff --git a/docs/env-vars.md b/docs/configuration/env-vars.md similarity index 99% rename from docs/env-vars.md rename to docs/configuration/env-vars.md index 436ccf7..6c05bd7 100644 --- a/docs/env-vars.md +++ b/docs/configuration/env-vars.md @@ -368,7 +368,7 @@ PAYMENT_HELEKET_TELEGRAM_EMOJI ## Поддержка -Подробный сценарий описан в [support.md](support.md). +Подробный сценарий описан в [features/support.md](../features/support.md). | Переменная | Назначение | | --- | --- | diff --git a/docs/configuration/security.md b/docs/configuration/security.md new file mode 100644 index 0000000..f0efab5 --- /dev/null +++ b/docs/configuration/security.md @@ -0,0 +1,37 @@ +# Безопасность + +Безопасность Minishop в первую очередь держится на стабильных секретах, корректном разделении публичных доменов и ограниченном доступе к админке. + +## Секреты + +- `WEBAPP_SESSION_SECRET` должен быть постоянным между рестартами, иначе Web App-сессии станут невалидными. +- `WEBHOOK_SECRET_TOKEN` защищает Telegram webhook. +- `PANEL_WEBHOOK_SECRET` проверяет входящие события Remnawave Panel. +- Платежные токены и webhook-секреты храните в `.env` или настройках админки с учетом доступа к серверу. + +Сгенерировать секрет можно так: + +```bash +openssl rand -hex 32 +``` + +## Доступ администраторов + +- `ADMIN_IDS` задает Telegram ID администраторов. +- Админка доступна только пользователям из `ADMIN_IDS` при входе через Telegram. +- Email-only аккаунты не получают админский доступ. + +## Публичные URL + +- `WEBHOOK_BASE_URL` должен вести на backend webhook server. +- `SUBSCRIPTION_MINI_APP_URL` должен вести на frontend/Mini App. +- Не добавляйте `/api`, `/auth` или webhook-пути в `SUBSCRIPTION_MINI_APP_URL`. + +## Дополнительно + +- Используйте HTTPS на всех публичных доменах. +- Ограничивайте доступ к серверу и `.env`. +- Следите за логами платежных вебхуков и panel webhooks. +- После ротации секретов перезапускайте соответствующие сервисы и проверяйте вебхуки. + +См. также [переменные окружения](env-vars.md) и [развертывание](../deployment.md). diff --git a/docs/deploy-examples/caddy.md b/docs/deploy-examples/caddy.md new file mode 100644 index 0000000..003da0c --- /dev/null +++ b/docs/deploy-examples/caddy.md @@ -0,0 +1,39 @@ +# Caddy + +Вариант `deploy/examples/caddy` подходит, если нужен самый простой публичный HTTPS. Caddy сам выпускает и продлевает сертификаты Let's Encrypt. + +## Требования + +- На сервере открыты входящие `80/tcp` и `443/tcp`. +- DNS-записи `WEBHOOK_HOST` и `MINIAPP_HOST` смотрят на этот сервер. +- В `.env` заполнены домены, токены, секреты и доступы к Remnawave. + +## Запуск + +```bash +cd deploy/examples/caddy +cp .env.example .env +nano .env +docker compose up -d +``` + +Минимально поменяйте: + +- `WEBHOOK_HOST` и `MINIAPP_HOST`; +- `BOT_TOKEN`, `ADMIN_IDS`; +- `POSTGRES_PASSWORD`; +- `WEBAPP_SESSION_SECRET`, `WEBHOOK_SECRET_TOKEN`; +- `PANEL_API_URL`, `PANEL_API_KEY`, `PANEL_WEBHOOK_SECRET`. + +## Проверка + +```bash +docker compose ps +docker compose logs -f caddy backend worker frontend +``` + +Если нужна нестандартная логика Caddy, правьте `deploy/examples/caddy/Caddyfile` и перезапускайте: + +```bash +docker compose up -d --force-recreate caddy +``` diff --git a/docs/deploy-examples/index.md b/docs/deploy-examples/index.md new file mode 100644 index 0000000..94b9f17 --- /dev/null +++ b/docs/deploy-examples/index.md @@ -0,0 +1,39 @@ +# Deploy examples + +В `deploy/examples` лежат самодостаточные Compose-варианты для разных способов публикации Minishop. Каждый пример запускается из своей директории и содержит собственный `docker-compose.yml`, `.env.example` и README. + +```bash +cp .env.example .env +nano .env +docker compose up -d +``` + +После старта проверяйте: + +```bash +docker compose ps +docker compose logs -f backend worker frontend +``` + +## Какой вариант выбрать + +| Вариант | Когда использовать | Где лежит | +| --- | --- | --- | +| [Caddy](caddy.md) | Нужен самый простой публичный HTTPS с автоматическими сертификатами Let's Encrypt. | `deploy/examples/caddy` | +| [Nginx](nginx.md) | Уже используете Nginx и готовы положить TLS-сертификаты рядом с примером. | `deploy/examples/nginx` | +| [Pangolin/Newt](newt.md) | Публикуете сервисы через туннель без входящих портов на сервере приложения. | `deploy/examples/newt` | +| [No proxy](no-proxy.md) | Нужно напрямую открыть порты backend/frontend или проверить стек без reverse proxy. | `deploy/examples/no-proxy` | + +## Два публичных URL + +Для production обычно нужны два домена: + +- webhook/backend URL для Telegram, платежных систем и Remnawave webhooks; +- Mini App/frontend URL для Telegram Mini App, Web App и админки. + +Пример: + +```text +https://webhooks.example.com -> backend:8080 +https://app.example.com -> frontend:80 +``` diff --git a/docs/deploy-examples/newt.md b/docs/deploy-examples/newt.md new file mode 100644 index 0000000..b7a69be --- /dev/null +++ b/docs/deploy-examples/newt.md @@ -0,0 +1,38 @@ +# Pangolin / Newt + +Вариант `deploy/examples/newt` подходит, если сервер приложения не должен принимать входящие соединения. Newt подключается к Pangolin, а публичные домены настраиваются ресурсами в панели Pangolin. + +## Запуск + +```bash +cd deploy/examples/newt +cp .env.example .env +nano .env +docker compose up -d +``` + +В `.env` заполните: + +- `WEBHOOK_HOST` и `MINIAPP_HOST` - публичные домены ресурсов в Pangolin; +- `PANGOLIN_ENDPOINT`, `NEWT_ID`, `NEWT_SECRET` - значения из настроек site/client в Pangolin; +- обычные переменные приложения: `BOT_TOKEN`, `ADMIN_IDS`, `POSTGRES_PASSWORD`, секреты и доступ к Remnawave. + +## Ресурсы Pangolin + +Создайте два HTTP-ресурса для Newt site: + +| Публичный домен | Upstream | +| --- | --- | +| `https://webhooks.example.com` | `http://backend:8080` | +| `https://app.example.com` | `http://frontend:80` | + +Домены в Pangolin должны совпадать с `WEBHOOK_HOST` и `MINIAPP_HOST`. + +Официальная инструкция Pangolin по установке Newt site: . + +## Проверка + +```bash +docker compose ps +docker compose logs -f newt backend worker frontend +``` diff --git a/docs/deploy-examples/nginx.md b/docs/deploy-examples/nginx.md new file mode 100644 index 0000000..0555914 --- /dev/null +++ b/docs/deploy-examples/nginx.md @@ -0,0 +1,44 @@ +# Nginx + +Вариант `deploy/examples/nginx` поднимает Nginx в той же Docker-сети, что и приложение. Он подходит, если у вас уже есть TLS-сертификаты или нужен ручной контроль Nginx-конфига. + +## Маршрутизация + +- `WEBHOOK_HOST` проксируется в `backend:8080`. +- `MINIAPP_HOST` проксируется в `frontend:80`. +- `frontend` сам проксирует внутренние `/api`, `/auth` и ассеты тем в `backend:8081`. + +## Подготовка + +```bash +cd deploy/examples/nginx +cp .env.example .env +nano .env +``` + +Положите TLS-сертификаты в `ssl/`: + +```text +ssl/ + webhooks.example.com/ + fullchain.pem + privkey.pem + app.example.com/ + fullchain.pem + privkey.pem +``` + +Имена папок должны совпадать с `WEBHOOK_HOST` и `MINIAPP_HOST` в `.env`. + +## Запуск + +```bash +docker compose up -d +docker compose logs -f nginx backend worker frontend +``` + +Если нужно поменять заголовки, лимиты или TLS-настройки, правьте `deploy/examples/nginx/nginx.conf.template` и перезапускайте: + +```bash +docker compose up -d --force-recreate nginx +``` diff --git a/docs/deploy-examples/no-proxy.md b/docs/deploy-examples/no-proxy.md new file mode 100644 index 0000000..871b0b0 --- /dev/null +++ b/docs/deploy-examples/no-proxy.md @@ -0,0 +1,35 @@ +# Без reverse proxy + +Вариант `deploy/examples/no-proxy` напрямую публикует HTTP-порты backend и frontend. Он удобен для локальной проверки, внутренней сети или ситуации, когда HTTPS завершается внешней платформой. + +## Порты + +- backend/webhooks: `WEB_SERVER_BIND`, по умолчанию `0.0.0.0:8080`; +- frontend/Mini App: `FRONTEND_BIND`, по умолчанию `0.0.0.0:8082`. + +## Запуск + +```bash +cd deploy/examples/no-proxy +cp .env.example .env +nano .env +docker compose up -d +``` + +## Важно про HTTPS + +Контейнеры приложения сами не выпускают TLS-сертификаты. Для реального Telegram webhook и Mini App публичные URL должны быть HTTPS. + +Используйте этот вариант, если: + +- проверяете стек локально; +- публикуете сервисы только во внутренней сети; +- TLS уже завершается внешним reverse proxy, load balancer или платформой. + +## Проверка + +```bash +curl http://127.0.0.1:8080/healthz +curl http://127.0.0.1:8082/health +docker compose logs -f backend worker frontend +``` diff --git a/docs/deployment.md b/docs/deployment.md index 853a8f9..e59af1a 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,7 +1,7 @@ # Развертывание Документ описывает продакшен-запуск после разделения проекта на `backend`, `frontend` и `worker`. -Перед стартом заполните минимальный `.env` по [configuration.md](configuration.md). Полный справочник переменных лежит в [env-vars.md](env-vars.md); после первого входа большинство продуктовых настроек удобнее менять через Web App админку. +Перед стартом заполните минимальный `.env` по [configuration.md](configuration.md). Полный справочник переменных лежит в [configuration/env-vars.md](configuration/env-vars.md); после первого входа большинство продуктовых настроек удобнее менять через Web App админку. ## Быстрый старт @@ -27,16 +27,16 @@ docker compose logs -f backend worker frontend ## Готовые папки запуска -Для production удобнее использовать не корневой compose, а отдельные примеры в -[`deploy/examples`](../deploy/examples). В каждой папке лежат свой `docker-compose.yml`, -`.env.example`, README и нужный конфиг рядом: +Для production удобнее использовать не корневой compose, а отдельные примеры из +[Deploy examples](deploy-examples/index.md). В каждой папке лежат свой `docker-compose.yml`, +`.env.example` и нужный конфиг рядом, а подробные инструкции хранятся в `docs/`: | Папка | Назначение | Запуск | | --- | --- | --- | -| [`deploy/examples/caddy`](../deploy/examples/caddy) | Caddy с автоматическим HTTPS. | `cp .env.example .env`, заполнить `.env`, `docker compose up -d`. | -| [`deploy/examples/nginx`](../deploy/examples/nginx) | Nginx в Docker-сети приложения, TLS-сертификаты кладутся в `ssl/`. | `cp .env.example .env`, заполнить `.env`, положить сертификаты, `docker compose up -d`. | -| [`deploy/examples/newt`](../deploy/examples/newt) | Pangolin/Newt без входящих портов на сервере приложения. | `cp .env.example .env`, заполнить Newt credentials, создать ресурсы в Pangolin, `docker compose up -d`. | -| [`deploy/examples/no-proxy`](../deploy/examples/no-proxy) | Прямая публикация портов backend/frontend. | `cp .env.example .env`, заполнить публичные URL и порты, `docker compose up -d`. | +| [Caddy](deploy-examples/caddy.md) | Caddy с автоматическим HTTPS. | `cp .env.example .env`, заполнить `.env`, `docker compose up -d`. | +| [Nginx](deploy-examples/nginx.md) | Nginx в Docker-сети приложения, TLS-сертификаты кладутся в `ssl/`. | `cp .env.example .env`, заполнить `.env`, положить сертификаты, `docker compose up -d`. | +| [Pangolin/Newt](deploy-examples/newt.md) | Pangolin/Newt без входящих портов на сервере приложения. | `cp .env.example .env`, заполнить Newt credentials, создать ресурсы в Pangolin, `docker compose up -d`. | +| [No proxy](deploy-examples/no-proxy.md) | Прямая публикация портов backend/frontend. | `cp .env.example .env`, заполнить публичные URL и порты, `docker compose up -d`. | Пример для Caddy: @@ -84,7 +84,7 @@ docker compose logs migrate - `redis`: Redis 7 для FSM, кеша, rate-limit, очередей и locks. В production-примерах внешний доступ добавляют `caddy`, `nginx`, `newt` или прямые `ports` в -соответствующей папке из [`deploy/examples`](../deploy/examples). +соответствующем варианте из [Deploy examples](deploy-examples/index.md). ## Логи и проверка @@ -246,9 +246,9 @@ docker compose up -d backend worker Готовые reverse-proxy примеры лежат в: -- [`deploy/examples/caddy`](../deploy/examples/caddy) - Caddy, автоматический HTTPS; -- [`deploy/examples/nginx`](../deploy/examples/nginx) - Nginx, сертификаты кладутся рядом в `ssl/`; -- [`deploy/examples/newt`](../deploy/examples/newt) - Newt/Pangolin, без входящих портов на сервере приложения. +- [Caddy](deploy-examples/caddy.md) - автоматический HTTPS; +- [Nginx](deploy-examples/nginx.md) - сертификаты кладутся рядом в `ssl/`; +- [Newt/Pangolin](deploy-examples/newt.md) - без входящих портов на сервере приложения. Во всех вариантах схема одинаковая: @@ -274,7 +274,7 @@ app.example.com { ## Newt -Для Newt используйте [`deploy/examples/newt`](../deploy/examples/newt). В compose уже есть сервис +Для Newt используйте [Pangolin / Newt](deploy-examples/newt.md). В compose уже есть сервис `newt`, а в `.env.example` - поля `PANGOLIN_ENDPOINT`, `NEWT_ID` и `NEWT_SECRET`. В Pangolin создайте два HTTP-ресурса для этого Newt site: diff --git a/docs/admin.md b/docs/features/admin-panel.md similarity index 100% rename from docs/admin.md rename to docs/features/admin-panel.md diff --git a/docs/features/core.md b/docs/features/core.md new file mode 100644 index 0000000..a9ea65f --- /dev/null +++ b/docs/features/core.md @@ -0,0 +1,22 @@ +# Основные возможности + +Minishop закрывает путь от регистрации пользователя до оплаты, продления, поддержки и сопровождения подписки. + +## Для пользователей + +- Регистрация через Telegram Mini App или email-код. +- Просмотр подписки, срока действия, трафика и ссылки подключения. +- Покупка подписки, пакетов трафика и дополнительных устройств. +- Пробный период, промокоды и реферальные сценарии. +- Тикеты поддержки внутри Mini App. +- Встроенные инструкции установки и публичные ссылки `/s/`. + +## Для администраторов + +- Поиск и управление пользователями. +- Настройка платежей, тарифов, внешнего вида и поддержки. +- Рассылки, промокоды и логи действий. +- Ручная синхронизация с Remnawave Panel. +- Редактор JSON-каталога тарифов. + +Подробности: [админ-панель](admin-panel.md), [Mini App](web-app.md) и [поддержка](support.md). diff --git a/docs/features/payments.md b/docs/features/payments.md new file mode 100644 index 0000000..09f922a --- /dev/null +++ b/docs/features/payments.md @@ -0,0 +1,28 @@ +# Платежи + +Платежные методы включаются настройками и отображаются пользователю как кнопки оплаты в Mini App и Telegram-сценариях. + +## Поддерживаемые провайдеры + +- [YooKassa](../payments/yookassa.md) +- [FreeKassa](../payments/freekassa.md) +- [Platega](../payments/platega.md) +- [SeverPay](../payments/severpay.md) +- [Wata](../payments/wata.md) +- [CryptoPay](../payments/cryptopay.md) +- [Heleket](../payments/heleket.md) +- [Telegram Stars](../payments/telegram-stars.md) + +## Типовой порядок настройки + +1. Включите нужный провайдер в админке или через `.env`. +2. Заполните публичные параметры и секреты. +3. Настройте webhook URL у провайдера, если это требуется. +4. Проверьте порядок и подписи кнопок оплаты. +5. Выполните тестовый платеж и проверьте логи backend. + +## Где смотреть параметры + +- [Справочник `.env`](../configuration/env-vars.md) содержит все ключи провайдеров. +- [Админ-панель](admin-panel.md) описывает UI-настройки платежей. +- [Тарифы](tariffs.md) описывают цены, Stars и сценарии покупки. diff --git a/docs/features/subscriptions.md b/docs/features/subscriptions.md new file mode 100644 index 0000000..1739c6f --- /dev/null +++ b/docs/features/subscriptions.md @@ -0,0 +1,21 @@ +# Подписки + +Подписки управляются через каталог тарифов и синхронизируются с Remnawave Panel. + +## Модели тарифов + +- **Period** - подписка на срок с месячным лимитом трафика. +- **Traffic** - покупка пакетов трафика без привязки к периоду. +- **Premium** - отдельные premium-сквады и premium-лимит. +- **HWID-устройства** - докупка дополнительных устройств при включенном разделе устройств. + +## Жизненный цикл + +- создание пользователя в панели; +- применение пробного периода или покупки; +- продление и докупки; +- предупреждения по трафику; +- синхронизация подписки и статусов; +- обработка смены тарифа. + +Подробности: [тарифы](tariffs.md) и [Mini App](web-app.md). diff --git a/docs/support.md b/docs/features/support.md similarity index 96% rename from docs/support.md rename to docs/features/support.md index a57effd..94b8e91 100644 --- a/docs/support.md +++ b/docs/features/support.md @@ -64,7 +64,7 @@ Email-уведомления администраторам включаются | `SUPPORT_ADMIN_NOTIFICATION_COOLDOWN_SECONDS` | Минимальная пауза между повторными Telegram/log уведомлениями по одному непрочитанному тикету. | | `SUPPORT_ADMIN_EMAIL_COOLDOWN_SECONDS` | Минимальная пауза между повторными email-уведомлениями по одному непрочитанному тикету. | -Все эти параметры описаны в [env-vars.md](env-vars.md). Основной рекомендуемый способ менять их - админка **Система -> Настройки -> Поддержка**; значения применяются как override поверх `.env`. +Все эти параметры описаны в [env-vars.md](../configuration/env-vars.md). Основной рекомендуемый способ менять их - админка **Система -> Настройки -> Поддержка**; значения применяются как override поверх `.env`. ## API и хранение diff --git a/docs/tariffs.md b/docs/features/tariffs.md similarity index 99% rename from docs/tariffs.md rename to docs/features/tariffs.md index b85ca81..0930ce3 100644 --- a/docs/tariffs.md +++ b/docs/features/tariffs.md @@ -5,7 +5,7 @@ - JSON-каталог тарифов из `TARIFFS_CONFIG_PATH` (по умолчанию `data/tariffs.json`); - конфигурация через переменные `.env`, если JSON-файл отсутствует. -JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: [data/tariffs.example.json](../data/tariffs.example.json). +JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: [data/tariffs.example.json](https://gitlab.com/3252a8/remnawave-minshop/-/blob/main/data/tariffs.example.json). Коротко по моделям: @@ -30,7 +30,7 @@ JSON-каталог может содержать несколько тариф После сохранения изменения применяются к новым запросам Web App сразу, потому что конфиг тарифов загружается из JSON при обращении. Уже созданные подписки сохраняют свой `tariff_key`; при удалении или отключении тарифа проверьте, что активные подписки с этим ключом не требуют дальнейшего продления или смены. -Подробности по админ-панели, правам доступа, сохранению настроек и списку разделов есть в [admin.md](admin.md). +Подробности по админ-панели, правам доступа, сохранению настроек и списку разделов есть в [админ-панели](admin-panel.md). ## Как выбирается режим diff --git a/docs/webapp.md b/docs/features/web-app.md similarity index 94% rename from docs/webapp.md rename to docs/features/web-app.md index ebf4eea..a97a4c7 100644 --- a/docs/webapp.md +++ b/docs/features/web-app.md @@ -16,7 +16,7 @@ Web App собирается в отдельный `frontend` image и отда - реферальную ссылку и статистику приглашений; - привязку email и Telegram к одному аккаунту. -Для администраторов из `ADMIN_IDS` Web App также показывает админ-панель: статистику, **пользователей** (поиск, фильтры, premium-трафик), поддержку, рассылки, промокоды, логи, настройки и редактор тарифов. Подробности: [admin.md](admin.md). +Для администраторов из `ADMIN_IDS` Web App также показывает админ-панель: статистику, **пользователей** (поиск, фильтры, premium-трафик), поддержку, рассылки, промокоды, логи, настройки и редактор тарифов. Подробности: [админ-панель](admin-panel.md). ## Настройки `.env` @@ -118,7 +118,7 @@ Email-вход работает через одноразовый код: Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL. -Полный список переменных, обязательные поля для включения email-входа и типичные ошибки подключения описаны в разделе **SMTP и вход по email** в [configuration.md](configuration.md). +Полный список переменных, обязательные поля для включения email-входа и типичные ошибки подключения описаны в разделе **SMTP и вход по email** в [configuration.md](../configuration.md). ## Проксирование @@ -131,12 +131,12 @@ Email-вход работает через одноразовый код: WebApp API на `backend:8081`, поэтому внешний reverse proxy обычно не должен отправлять эти пути в `backend:8081` напрямую. -Готовые примеры лежат в [`deploy/examples`](../deploy/examples): +Готовые варианты описаны в [Deploy examples](../deploy-examples/index.md): -- `caddy` - Caddy с автоматическим HTTPS; -- `nginx` - Nginx с сертификатами в соседней папке `ssl/`; -- `newt` - Pangolin/Newt; -- `no-proxy` - прямая публикация портов для проверки или внешней TLS-платформы. +- [Caddy](../deploy-examples/caddy.md) - автоматический HTTPS; +- [Nginx](../deploy-examples/nginx.md) - сертификаты в соседней папке `ssl/`; +- [Pangolin/Newt](../deploy-examples/newt.md) - публикация без входящих портов на сервере приложения; +- [No proxy](../deploy-examples/no-proxy.md) - прямая публикация портов для проверки или внешней TLS-платформы. В default `docker-compose.yml` наружу публикуются `frontend` и webhook/backend port, а внутри Docker network сервисы доступны друг другу по service DNS names: diff --git a/docs/webapp-themes.md b/docs/features/webapp-themes.md similarity index 100% rename from docs/webapp-themes.md rename to docs/features/webapp-themes.md diff --git a/docs/webapp-themes.webp b/docs/features/webapp-themes.webp similarity index 100% rename from docs/webapp-themes.webp rename to docs/features/webapp-themes.webp diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md new file mode 100644 index 0000000..6470c14 --- /dev/null +++ b/docs/getting-started/overview.md @@ -0,0 +1,26 @@ +# Обзор + +Remnawave Minishop состоит из Telegram-бота, backend API, worker-процессов, frontend/Mini App и инфраструктурных сервисов PostgreSQL и Redis. В production эти части запускаются через Docker Compose и общаются с Remnawave Panel по API и вебхукам. + +## Основные компоненты + +- **Backend** - Telegram webhook, платежные вебхуки, panel webhooks, API для Mini App и админки. +- **Worker** - фоновые задачи, синхронизация подписок, обработка очереди вебхуков и тарифных событий. +- **Frontend** - отдельный nginx-образ с Mini App и админкой. +- **PostgreSQL** - пользователи, платежи, настройки, поддержка, промокоды и служебные данные. +- **Redis** - FSM, кеши, rate limit, очередь вебхуков и distributed locks. + +## Сценарии + +- пользователь открывает Mini App, видит подписку и оплачивает тариф; +- платежный провайдер отправляет webhook в backend; +- worker применяет фоновые задачи и синхронизацию; +- Remnawave Panel хранит пользователя, подписку и ссылку подключения; +- администратор управляет тарифами, поддержкой, пользователями и настройками через админку. + +## Куда идти дальше + +- [Установка](setup.md) - базовый запуск через Compose. +- [Deploy examples](../deploy-examples/index.md) - готовые варианты публикации. +- [Архитектура](../architecture.md) - структура каталогов и сервисов. +- [Mini App](../features/web-app.md) - публичный frontend, Telegram OAuth и инструкции установки. diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md new file mode 100644 index 0000000..ee034d0 --- /dev/null +++ b/docs/getting-started/setup.md @@ -0,0 +1,38 @@ +# Установка + +Начните с `.env`, затем поднимите Compose-стек и проверьте backend, worker и frontend. + +## Минимальный запуск + +```bash +cp .env.example .env +nano .env +docker compose up -d --build +docker compose ps +docker compose logs -f backend worker frontend +``` + +## Что заполнить в первую очередь + +- `BOT_TOKEN` и `ADMIN_IDS` для доступа к боту и админке. +- `WEBHOOK_BASE_URL` для Telegram, платежных и panel webhook URL. +- `SUBSCRIPTION_MINI_APP_URL` для Mini App и кнопок в Telegram. +- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`. +- `WEBAPP_SESSION_SECRET`, `WEBHOOK_SECRET_TOKEN`, `PANEL_API_URL`, `PANEL_API_KEY`, `PANEL_WEBHOOK_SECRET`. + +## Как выбрать Compose-вариант + +- Для быстрого публичного HTTPS берите [Caddy](../deploy-examples/caddy.md). +- Если у вас уже есть TLS-сертификаты и нужен Nginx в Docker-сети, берите [Nginx](../deploy-examples/nginx.md). +- Если нельзя открывать входящие порты на сервере приложения, берите [Pangolin/Newt](../deploy-examples/newt.md). +- Для локальной проверки или внешнего TLS-терминатора берите [no-proxy](../deploy-examples/no-proxy.md). + +## После первого входа + +1. Откройте админку через Mini App. +2. Проверьте платежные методы в настройках. +3. Настройте каталог тарифов. +4. Проверьте инструкции подключения. +5. Сделайте тестовую покупку или пробную активацию. + +Подробности: [настройка окружения](../configuration.md) и [развертывание](../deployment.md). diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b852c2c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +# Remnawave Minishop + +Remnawave Minishop - Telegram-бот и Mini App для продажи и управления подписками Remnawave. Документация помогает развернуть стек, настроить платежи, тарифы, админку, поддержку и публичный личный кабинет. + +> Проект работает вместе с Remnawave Panel: панель хранит пользователей и подписки, а Minishop отвечает за Telegram, платежи, Mini App, тарифы и операционную админку. + +## Быстрый старт + +- [Обзор](getting-started/overview.md) - архитектура, сервисы и основные сценарии. +- [Установка](getting-started/setup.md) - путь от `.env` до первого запуска. +- [Deploy examples](deploy-examples/index.md) - Caddy, Nginx, Pangolin/Newt и no-proxy варианты. +- [Настройка платежей](features/payments.md) - включение провайдеров и проверка вебхуков. +- [Безопасность](configuration/security.md) - секреты, доступы и публичные URL. +- [Админ-панель](features/admin-panel.md) - пользователи, настройки, рассылки, поддержка и тарифы. +- [Миграции](migrations/index.md) - готовые сценарии переноса с других ботов. +- [Устранение неполадок](troubleshooting/issues.md) - быстрые проверки для частых проблем. + +## Ключевые возможности + +- **Продажа подписок** - period- и traffic-тарифы, докупки трафика, HWID-устройства, premium-сквады и Telegram Stars. +- **Жизненный цикл пользователей** - регистрация, пробный период, продление, синхронизация с панелью и предупреждения по трафику. +- **Mini App** - личный кабинет, инструкции установки, Telegram OAuth, email-вход и публичные referral-ссылки. +- **Операционные инструменты** - админка, тикеты поддержки, промокоды, рассылки, логи и настройки поверх `.env`. + +## Справочник + +- [Переменные окружения](configuration/env-vars.md) +- [Развертывание](deployment.md) +- [Тарифы](features/tariffs.md) +- [Темы Web App](features/webapp-themes.md) +- [Миграции](migrations/index.md) +- [Миграция с remnawave-tg-shop](migrations/remnawave-tg-shop.md) diff --git a/docs/migrations/index.md b/docs/migrations/index.md new file mode 100644 index 0000000..36e2add --- /dev/null +++ b/docs/migrations/index.md @@ -0,0 +1,27 @@ +# Миграции с других ботов + +Этот раздел содержит готовые инструкции миграции в Remnawave Minishop из уже описанных источников. Каждая поддерживаемая миграция должна быть отдельным Markdown-файлом с конкретными шагами, ограничениями, командами и проверками. + +Сейчас в документации есть только один готовый сценарий: + +| Источник | Поддерживаемый случай | Инструкция | +| --- | --- | --- | +| `remnawave-tg-shop` `v2.7.0` и близкие версии | Переезд старого stack/volume PostgreSQL на split-архитектуру Minishop `v3.4+`, обновление `.env`, запуск `migrate`, проверка reverse proxy | [Миграция с remnawave-tg-shop](remnawave-tg-shop.md) | + +## Что покрывает текущая миграция + +Инструкция для `remnawave-tg-shop` рассчитана на родственный стек, где заранее известны Docker volumes, контейнеры, схема БД и путь обновления: + +- перенос PostgreSQL volume `remnawave-tg-shop-db-data` в новый volume Minishop; +- создание новых пустых volumes `redis-data` и `shop-data`; +- перенос Caddy volumes при использовании Caddy-варианта; +- обновление переменных окружения, которые изменились после `v2.7.0`; +- запуск one-shot сервиса `migrate`; +- переход с одного upstream `remnawave-tg-shop:8000` на `backend:8080` и `frontend:80`; +- запуск через корневой compose или готовые deploy examples. + +## Что пока не описано + +Для других Telegram-ботов, самописных панелей и ручных таблиц готовой инструкции пока нет. Такие источники нельзя переносить по инструкции `remnawave-tg-shop`: у них могут отличаться таблицы пользователей, модель тарифов, статусы платежей, связь с Remnawave Panel, формат промокодов, рефералы и правила отката. + +Когда для конкретного источника появится проверенный сценарий, он должен быть добавлен в этот раздел отдельным файлом и отдельной строкой в таблице выше. diff --git a/docs/migration-to-minishop.md b/docs/migrations/remnawave-tg-shop.md similarity index 94% rename from docs/migration-to-minishop.md rename to docs/migrations/remnawave-tg-shop.md index a5ee041..f2b54ed 100644 --- a/docs/migration-to-minishop.md +++ b/docs/migrations/remnawave-tg-shop.md @@ -1,5 +1,9 @@ # Миграция с `remnawave-tg-shop` (≤ v2.7.0) на `remnawave-minishop` (v3.4+) +Эта страница - готовый сценарий для legacy-стека `remnawave-tg-shop`. Это единственная миграция с другого бота, которая сейчас описана в документации. Для других Telegram-ботов, самописных панелей и ручных таблиц готового сценария пока нет: их нельзя переносить по этой инструкции без отдельного анализа схемы БД, тарифов, платежей и связи с Remnawave Panel. + +Автоматический скрипт ниже рассчитан именно на родственный стек `remnawave-tg-shop`, где структура БД и Docker volumes известны заранее. Для других ботов нужен отдельный адаптер экспорта/импорта. + ## Короткий путь без смены ветки и сборки Если вы используете только готовые Docker-образы и не собираете проект @@ -127,7 +131,7 @@ docker compose \ | — | `REDIS_URL=redis://redis:6379/0` | Обязательна для воркера, очередей и rate-limit. По умолчанию в compose-файлах уже задана. | | — | `WEBAPP_SESSION_SECRET`, `WEBAPP_ENABLED`, `WEBAPP_SERVER_PORT`, `WEBAPP_THEMES_DIR`, `TARIFFS_CONFIG_PATH` | Новые настройки Web App / тарифного каталога. Безопасные дефолты есть в `.env.example`. | -Полный референс — [docs/configuration.md](configuration.md). Скрипт миграции +Полный референс — [docs/configuration.md](../configuration.md). Скрипт миграции эти переменные **не правит** автоматически (только `POSTGRES_HOST`), потому что у каждой инсталляции свой шаблон `.env` с кастомными значениями. Лучше сравнить свой `.env` с `.env.example` глазами один раз, чем получить @@ -357,8 +361,8 @@ server { ``` Полные примеры (Caddy, Nginx, Newt/Pangolin и запуск без reverse proxy) — в -[docs/deployment.md](deployment.md), [docs/webapp.md](webapp.md) и папке -[`deploy/examples`](../deploy/examples). Если раньше прокси указывал на +[docs/deployment.md](../deployment.md), [docs/features/web-app.md](../features/web-app.md) и +[Deploy examples](../deploy-examples/index.md). Если раньше прокси указывал на `remnawave-tg-shop:8000` напрямую, после миграции нужно либо переключиться на `backend:8080` / `frontend:80`, либо использовать готовый Caddy/Nginx/Newt пример, который уже знает правильную маршрутизацию. diff --git a/docs/payments/cryptopay.md b/docs/payments/cryptopay.md new file mode 100644 index 0000000..2cb2235 --- /dev/null +++ b/docs/payments/cryptopay.md @@ -0,0 +1,28 @@ +# CryptoPay + +CryptoPay используется для криптовалютных платежей через отдельный токен и сеть Crypto Bot API. + +## Что включить + +- `CRYPTOPAY_ENABLED` - включает CryptoPay среди доступных методов. +- Presentation-ключи `PAYMENT_CRYPTOPAY_*` - подписи и иконки кнопки в Mini App и Telegram. + +## Что настроить + +1. Укажите `CRYPTOPAY_TOKEN`. +2. Выберите `CRYPTOPAY_NETWORK`: `mainnet` или `testnet`. +3. Задайте `CRYPTOPAY_CURRENCY_TYPE`: `fiat` или `crypto`. +4. Проверьте `CRYPTOPAY_ASSET`, например `RUB`, `USDT` или `BTC`. +5. Добавьте `cryptopay` в `PAYMENT_METHODS_ORDER`. + +## Проверка + +- Для тестов используйте соответствующую сеть: testnet-токен не должен попадать в mainnet-настройки. +- Выполните тестовый платеж и проверьте, что статус закрывается после callback от провайдера. +- Если сумма или asset выглядят неверно, проверьте сочетание `CRYPTOPAY_CURRENCY_TYPE` и `CRYPTOPAY_ASSET`. + +## Где подробнее + +- [Переменные CryptoPay](../configuration/env-vars.md#cryptopay) +- [Настройка платежей](../features/payments.md) +- [Логи и диагностика](../troubleshooting/logs.md) diff --git a/docs/payments/freekassa.md b/docs/payments/freekassa.md new file mode 100644 index 0000000..9c8d020 --- /dev/null +++ b/docs/payments/freekassa.md @@ -0,0 +1,16 @@ +# FreeKassa + +FreeKassa подключается как отдельный платежный метод и обрабатывает входящие webhook-события через backend. + +## Что настроить + +- Включение провайдера: `FREEKASSA_ENABLED`. +- ID магазина, API/secret-ключи и настройки подписи. +- Trusted IP allowlist, если используется. +- Публичный webhook URL на `WEBHOOK_BASE_URL`. + +## Где подробнее + +- [Переменные FreeKassa](../configuration/env-vars.md#freekassa) +- [Платежи](../features/payments.md) +- [Логи и проверка](../troubleshooting/logs.md) diff --git a/docs/payments/heleket.md b/docs/payments/heleket.md new file mode 100644 index 0000000..3fc183f --- /dev/null +++ b/docs/payments/heleket.md @@ -0,0 +1,31 @@ +# Heleket + +Heleket используется для crypto-инвойсов с отдельными merchant ID, payment API key, валютой инвойса и настройками webhook-проверки. + +## Что включить + +- `HELEKET_ENABLED` - включает Heleket среди доступных методов. +- Presentation-ключи `PAYMENT_HELEKET_*` - подписи и иконки кнопки. + +## Что настроить + +1. Укажите `HELEKET_BASE_URL`, `HELEKET_MERCHANT_ID` и `HELEKET_API_KEY`. +2. Настройте `HELEKET_CURRENCY`. +3. При необходимости задайте `HELEKET_TO_CURRENCY` и `HELEKET_NETWORK`. +4. Проверьте `HELEKET_RETURN_URL` и `HELEKET_SUCCESS_URL`. +5. Настройте `HELEKET_LIFETIME_SECONDS`: допустимый диапазон 300..43200. +6. Если включаете проверку webhook, задайте `HELEKET_VERIFY_WEBHOOK_SIGNATURE`. +7. Для IP-фильтрации заполните `HELEKET_TRUSTED_IPS`. +8. Добавьте `heleket` в `PAYMENT_METHODS_ORDER`. + +## Проверка + +- Создайте тестовый инвойс и убедитесь, что пользователь получает корректную ссылку. +- Проверьте, что сеть и валюта соответствуют настройкам в кабинете Heleket. +- Если webhook отклоняется, проверьте подпись, allowlist и фактический payload в backend-логах. + +## Где подробнее + +- [Переменные Heleket](../configuration/env-vars.md#heleket) +- [Настройка платежей](../features/payments.md) +- [Логи и диагностика](../troubleshooting/logs.md) diff --git a/docs/payments/platega.md b/docs/payments/platega.md new file mode 100644 index 0000000..faab142 --- /dev/null +++ b/docs/payments/platega.md @@ -0,0 +1,30 @@ +# Platega + +Platega подключается как отдельный платежный провайдер, но внутри Minishop может дать несколько кнопок: основную legacy-кнопку, СБП/карту и крипто-кнопку. Общие merchant-параметры задаются один раз, а method ID и подписи кнопок настраиваются отдельно. + +## Что включить + +- `PLATEGA_ENABLED` - общий флаг провайдера. +- `PLATEGA_SBP_ENABLED` - отдельная кнопка СБП/карта. +- `PLATEGA_CRYPTO_ENABLED` - отдельная crypto-кнопка Platega. +- `PLATEGA_PAYMENT_METHOD` - legacy/fallback method ID для старых callback и старых установок. + +## Что настроить + +1. Укажите `PLATEGA_BASE_URL`, `PLATEGA_MERCHANT_ID` и `PLATEGA_SECRET`. +2. Заполните `PLATEGA_SBP_METHOD` и/или `PLATEGA_CRYPTO_METHOD`, если используете отдельные кнопки. +3. Проверьте `PLATEGA_RETURN_URL` и `PLATEGA_FAILED_URL`. +4. Настройте тексты и иконки кнопок через `PAYMENT_PLATEGA_SBP_*` и `PAYMENT_PLATEGA_CRYPTO_*`. +5. Добавьте нужные методы в `PAYMENT_METHODS_ORDER`. + +## Проверка + +- После сохранения настроек откройте Mini App и убедитесь, что видны только включенные Platega-кнопки. +- Выполните тестовую оплату для каждой включенной кнопки: СБП/карта и crypto используют разные method ID. +- При ошибках проверьте backend-логи и ответ провайдера при создании платежной ссылки. + +## Где подробнее + +- [Переменные Platega](../configuration/env-vars.md#platega) +- [Настройка платежей](../features/payments.md) +- [Логи и диагностика](../troubleshooting/logs.md) diff --git a/docs/payments/severpay.md b/docs/payments/severpay.md new file mode 100644 index 0000000..cd716fd --- /dev/null +++ b/docs/payments/severpay.md @@ -0,0 +1,28 @@ +# SeverPay + +SeverPay подключается как отдельный платежный метод с собственным MID, token и сроком жизни платежной ссылки. + +## Что включить + +- `SEVERPAY_ENABLED` - показывает SeverPay среди доступных методов оплаты. +- Presentation-ключи `PAYMENT_SEVERPAY_*` - подписи и иконки кнопки в Mini App и Telegram. + +## Что настроить + +1. Укажите `SEVERPAY_BASE_URL`. +2. Заполните `SEVERPAY_MID` и `SEVERPAY_TOKEN`. +3. Настройте `SEVERPAY_RETURN_URL`. +4. При необходимости задайте `SEVERPAY_LIFETIME_MINUTES`. +5. Добавьте `severpay` в `PAYMENT_METHODS_ORDER`. + +## Проверка + +- Создайте тестовый платеж и проверьте, что пользователь получает платежную ссылку. +- Убедитесь, что ссылка живет ожидаемое время, если задан `SEVERPAY_LIFETIME_MINUTES`. +- После оплаты проверьте статус платежа в backend-логах и в админке. + +## Где подробнее + +- [Переменные SeverPay](../configuration/env-vars.md#severpay) +- [Настройка платежей](../features/payments.md) +- [Логи и диагностика](../troubleshooting/logs.md) diff --git a/docs/payments/telegram-stars.md b/docs/payments/telegram-stars.md new file mode 100644 index 0000000..a14907e --- /dev/null +++ b/docs/payments/telegram-stars.md @@ -0,0 +1,19 @@ +# Telegram Stars + +Telegram Stars используются напрямую и поддерживаются в legacy-ценах и JSON-каталоге тарифов. + +## Где применяются Stars + +- Цены периодов подписки. +- Пакеты трафика. +- Premium-докупки. +- HWID-докупки, если они включены в каталоге тарифов. + +## Что проверить + +- `STARS_ENABLED`. +- Stars-цены в legacy-настройках или JSON-каталоге. +- Корректное округление цены до целого количества Stars. +- Сценарии смены тарифа: XTR/Stars-докупки не конвертируются без явного курса. + +Подробности: [переменные платежей](../configuration/env-vars.md#платежи) и [тарифы](../features/tariffs.md). diff --git a/docs/payments/wata.md b/docs/payments/wata.md new file mode 100644 index 0000000..777849e --- /dev/null +++ b/docs/payments/wata.md @@ -0,0 +1,30 @@ +# Wata + +Wata подключается как отдельный провайдер с bearer token, платежными ссылками и опциональной проверкой подписи webhook. + +## Что включить + +- `WATA_ENABLED` - включает Wata для пользователей. +- `WATA_ADMIN_ONLY_ENABLED` - оставляет метод доступным только для админских сценариев, если используется вместо публичного включения. +- Presentation-ключи `PAYMENT_WATA_*` - подписи и иконки кнопки. + +## Что настроить + +1. Укажите `WATA_BASE_URL` и `WATA_API_TOKEN`. +2. Проверьте `WATA_RETURN_URL` и `WATA_FAILED_URL`. +3. Настройте `WATA_LINK_TTL_MINUTES`: минимум 15 минут, максимум 43200. +4. Если включаете проверку подписи, задайте `WATA_WEBHOOK_VERIFY_SIGNATURE` и при необходимости `WATA_PUBLIC_KEY`. +5. Для дополнительной защиты заполните `WATA_TRUSTED_IPS`. +6. Добавьте `wata` в `PAYMENT_METHODS_ORDER`. + +## Проверка + +- Создайте тестовый платеж и убедитесь, что ссылка открывается у пользователя. +- Проверьте входящий webhook: подпись и IP-allowlist должны соответствовать фактическому запросу Wata. +- Если платеж остается в pending, проверьте backend-логи вокруг webhook и статуса ссылки. + +## Где подробнее + +- [Переменные Wata](../configuration/env-vars.md#wata) +- [Настройка платежей](../features/payments.md) +- [Логи и диагностика](../troubleshooting/logs.md) diff --git a/docs/payments/yookassa.md b/docs/payments/yookassa.md new file mode 100644 index 0000000..ef94066 --- /dev/null +++ b/docs/payments/yookassa.md @@ -0,0 +1,16 @@ +# YooKassa + +YooKassa используется для рублевых оплат и может участвовать в сценариях автопродления period-подписок. + +## Что настроить + +- Включение провайдера: `YOOKASSA_ENABLED`. +- Идентификаторы и секреты магазина. +- Webhook URL на backend-домен. +- Отображение кнопки оплаты и порядок платежных методов. + +## Где подробнее + +- [Переменные YooKassa](../configuration/env-vars.md#yookassa) +- [Платежи](../features/payments.md) +- [Тарифы и автопродление](../features/tariffs.md#автопродление-пробный-период-и-бонусы) diff --git a/docs/troubleshooting/issues.md b/docs/troubleshooting/issues.md new file mode 100644 index 0000000..de3aae9 --- /dev/null +++ b/docs/troubleshooting/issues.md @@ -0,0 +1,33 @@ +# Проблемы + +Начинайте диагностику с состояния контейнеров и логов, затем проверяйте публичные URL и секреты. + +## Стек не стартует + +- Проверьте `docker compose ps`. +- Посмотрите `docker compose logs migrate`. +- Убедитесь, что PostgreSQL и Redis здоровы. +- Проверьте обязательные переменные в `.env`. + +## Telegram webhook не работает + +- Проверьте `WEBHOOK_BASE_URL`. +- Убедитесь, что домен доступен по HTTPS. +- Проверьте `WEBHOOK_SECRET_TOKEN`. +- Посмотрите backend-логи на момент входящего события. + +## Mini App не открывается + +- Проверьте `SUBSCRIPTION_MINI_APP_URL`. +- Убедитесь, что URL указывает на frontend, а не на `/api` или webhook-домен. +- Проверьте настройки BotFather. +- Посмотрите frontend и backend-логи. + +## Платеж не засчитался + +- Проверьте включение провайдера. +- Проверьте webhook URL и секреты. +- Посмотрите backend-логи. +- Сверьте статус платежа в админке и кабинете провайдера. + +Подробности: [логи](logs.md) и [развертывание](../deployment.md). diff --git a/docs/troubleshooting/logs.md b/docs/troubleshooting/logs.md new file mode 100644 index 0000000..70f253e --- /dev/null +++ b/docs/troubleshooting/logs.md @@ -0,0 +1,104 @@ +# Логи + +Логи - главный источник диагностики при проблемах запуска, платежей, вебхуков и синхронизации с Remnawave Panel. + +## Основные команды + +```bash +docker compose logs -f backend +docker compose logs -f worker +docker compose logs -f frontend +docker compose logs migrate +``` + +## Что искать + +- ошибки миграций в `migrate`; +- ошибки Telegram webhook и payment webhook в `backend`; +- проблемы очереди вебхуков и фоновых задач в `worker`; +- ошибки проксирования `/api`, `/auth` и theme assets во `frontend`; +- ошибки авторизации Mini App и Telegram OAuth. + +## Frontend proxy, `/api`, `/auth` и theme assets + +`frontend` - это nginx-контейнер Mini App. Он отдает статику и проксирует Web App маршруты во внутренний backend WebApp server на `backend:8081`. + +Сначала смотрите nginx-логи: + +```bash +docker compose logs -f frontend +``` + +Если видите `404`, `502`, `upstream` или `connect() failed`, проверьте маршруты: + +- `/api/*` и `/auth/*` должны попадать в `frontend:80`, а уже frontend проксирует их в `backend:8081`; +- `/webapp-logo`, `/webapp-uploaded-logo/*`, `/webapp-favicon/*`, `/webapp-theme-css/*` и `/webapp-theme-assets/*` тоже проксируются через frontend; +- внешний reverse proxy не должен отдельно уводить `/api` или `/auth` на webhook-сервер `backend:8080`. + +Быстрые проверки снаружи: + +```bash +curl -i https://app.domain.com/health +curl -i https://app.domain.com/api/bootstrap +curl -i https://app.domain.com/auth/telegram/start +curl -i https://app.domain.com/webapp-theme-css/dark/style.css +``` + +Если `/health` отвечает, а `/api/bootstrap` или theme assets падают, смотрите одновременно frontend и backend: + +```bash +docker compose logs -f frontend backend +``` + +Где проверять конфигурацию: + +- frontend nginx: `deploy/docker/frontend/nginx.conf`; +- внешний Caddy/Nginx: `deploy/examples/caddy/Caddyfile` или `deploy/examples/nginx/nginx.conf.template`; +- Web App домен: `SUBSCRIPTION_MINI_APP_URL`, он должен быть публичным HTTPS URL frontend, без `/api`, `/auth` или webhook-пути; +- WebApp server backend: `WEBAPP_ENABLED=True`, `WEBAPP_SERVER_HOST=0.0.0.0`, `WEBAPP_SERVER_PORT=8081`. + +## Mini App auth и Telegram OAuth + +Ошибки авторизации почти всегда видны в `backend`, потому что проверка Telegram Mini Apps `initData`, Telegram OAuth `id_token`, nonce/state и сессий выполняется на backend WebApp server. + +```bash +docker compose logs -f backend +``` + +Ищите сообщения: + +- `Telegram WebApp initData hash mismatch`; +- `Telegram WebApp initData auth_date is stale`; +- `Failed to validate Telegram WebApp initData`; +- `Telegram OAuth nonce mismatch`; +- `Telegram OAuth ID token is stale`; +- `Failed to validate Telegram OAuth ID token`; +- `Telegram OAuth token exchange failed`; +- `Telegram OAuth callback failed`; +- `WebApp auth failed`. + +Для Mini App внутри Telegram проверьте: + +- `SUBSCRIPTION_MINI_APP_URL` совпадает с доменом, указанным в BotFather Mini Apps; +- открывается именно HTTPS frontend-домен, а не backend webhook-домен; +- время на сервере синхронизировано, иначе `auth_date is stale`; +- `WEBAPP_AUTH_MAX_AGE_SECONDS` не слишком маленький; +- `WEBAPP_SESSION_SECRET` постоянный между рестартами. + +Для Telegram OAuth вне Mini App проверьте: + +- `TELEGRAM_OAUTH_CLIENT_ID` и `TELEGRAM_OAUTH_CLIENT_SECRET`; +- callback в Telegram OAuth/BotFather: `https://app.domain.com/auth/telegram/callback`; +- `/auth/telegram/start` и `/auth/telegram/callback` проходят через frontend nginx в `backend:8081`; +- в браузере после callback нет статуса `telegram_auth=invalid_state`, `invalid_token`, `not_configured`, `unauthorized` или `failed`. + +Подробности по маршрутам и настройке OAuth: [Web App / Mini App](../features/web-app.md). + +## После изменения конфигурации + +```bash +docker compose up -d +docker compose logs -f backend worker frontend +``` + +См. также [проблемы](issues.md) и [развертывание](../deployment.md). diff --git a/tests/test_migration_doc_accuracy.py b/tests/test_migration_doc_accuracy.py index dafbfca..5af59d7 100644 --- a/tests/test_migration_doc_accuracy.py +++ b/tests/test_migration_doc_accuracy.py @@ -1,4 +1,4 @@ -"""Pin facts that ``docs/migration-to-minishop.md`` and +"""Pin facts that ``docs/migrations/remnawave-tg-shop.md`` and ``scripts/migrate_to_minishop.sh`` rely on. Both documents are written for a user upgrading from ``remnawave-tg-shop`` @@ -18,7 +18,7 @@ import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] -DOC_PATH = REPO_ROOT / "docs" / "migration-to-minishop.md" +DOC_PATH = REPO_ROOT / "docs" / "migrations" / "remnawave-tg-shop.md" SCRIPT_PATH = REPO_ROOT / "scripts" / "migrate_to_minishop.sh" COMPOSE_FILES = ( REPO_ROOT / "docker-compose.yml", @@ -64,14 +64,14 @@ class MigrationDocumentationFactsTests(unittest.TestCase): missing = sorted(name for name in EXPECTED_CONTAINER_NAMES if name not in self.doc) self.assertFalse( missing, - f"migration-to-minishop.md is missing container names from current compose: {missing}", + f"migrations/remnawave-tg-shop.md is missing container names from current compose: {missing}", ) def test_doc_lists_every_volume_in_current_compose(self): missing = sorted(name for name in EXPECTED_VOLUME_NAMES if name not in self.doc) self.assertFalse( missing, - f"migration-to-minishop.md is missing volume names from current compose: {missing}", + f"migrations/remnawave-tg-shop.md is missing volume names from current compose: {missing}", ) def test_doc_warns_about_renamed_telegram_webhook_secret(self): @@ -234,7 +234,7 @@ class DocComposeFileReferencesTests(unittest.TestCase): self.assertIn(relpath, doc) self.assertTrue( (REPO_ROOT / relpath).is_file(), - f"{relpath} is referenced in migration-to-minishop.md but missing on disk", + f"{relpath} is referenced in migrations/remnawave-tg-shop.md but missing on disk", ) def test_doc_references_migrator_module_path(self):