diff --git a/README.md b/README.md index d22bdc4..f5cb751 100644 --- a/README.md +++ b/README.md @@ -1,263 +1,153 @@ # Bedolaga Cabinet - Web Interface -Современный веб-интерфейс личного кабинета для VPN бота на базе [Remnawave Bedolaga Telegram Bot V3.0.0+](https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot). +Веб-интерфейс личного кабинета для VPN бота на базе [Remnawave Bedolaga Telegram Bot V3.0.0+](https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot). -## Возможности - -- 🔐 Авторизация через Telegram -- 💳 Пополнение баланса (YooKassa, CryptoBot, Stars и др.) -- 📊 Управление подписками и ключами -- 🎫 Система тикетов поддержки -- 🌐 Мультиязычность (EN/RU) -- 📱 Адаптивный дизайн -- 🎨 Настраиваемый брендинг -- Админпанель для управлени ботом -- Автоматическая передача subpage конфигов приложений из Remnawave -- ⚡ Fast - React + Vite + TypeScript +React + Vite + TypeScript | Авторизация через Telegram | Мультиязычность (EN/RU) | Адаптивный дизайн ## Требования -- Node.js 18+ (для разработки) -- Docker и Docker Compose (для production) +- Docker и Docker Compose - Запущенный backend бота с включенным Cabinet API +- Обратный прокси (Caddy / Nginx / Traefik) -## Быстрый старт +## Архитектура -### Вариант A: Готовый Docker образ - -```bash -docker pull ghcr.io/bedolaga-dev/bedolaga-cabinet:latest - -или - -docker pull bedolaga/bedolaga-cabinet:latest +``` +Браузер → Caddy/Nginx → /api/* → remnawave_bot:8080 (backend API) + → /* → /srv/cabinet (статика frontend) ``` -Затем настройте Caddy/Nginx для проксирования (см. раздел "Настройка прокси для production"). +Frontend — это статические файлы (HTML, JS, CSS). Обратный прокси выполняет две задачи: +1. Раздает статику frontend +2. Проксирует `/api/*` запросы на backend бота (с удалением префикса `/api`) -### Вариант B: Сборка из исходников +## Установка -#### 1. Клонирование репозитория +### Шаг 1. Настройка backend бота -```bash -git clone https://github.com/BEDOLAGA-DEV/bedolaga-cabinet.git -cd bedolaga-cabinet -``` - -#### 2. Настройка окружения - -**⚠️ ОБЯЗАТЕЛЬНО:** Скопируйте `.env.example` в `.env` перед запуском! - -```bash -cp .env.example .env -``` - -Docker Compose не запустится без `.env` файла. - -**Основные переменные:** - -```env -# API URL - путь к backend API -# Используйте /api если прокси на том же домене -# Или полный URL если backend на другом сервере -VITE_API_URL=/api - -# Telegram Bot Username (без @) -VITE_TELEGRAM_BOT_USERNAME=your_bot_username - -# Брендинг (опционально) -VITE_APP_NAME=My VPN Cabinet -VITE_APP_LOGO=V - -# Порт для Docker контейнера -CABINET_PORT=3020 -``` - -#### 3. Запуск в Docker - -```bash -docker compose up -d --build -``` - -Приложение будет доступно на `http://localhost:3020` - -## Настройка backend - -В `.env` файле вашего бота добавьте: +В `.env` файле бота добавьте: ```env # Включить Cabinet API CABINET_ENABLED=true -# JWT секрет для авторизации (сгенерируйте случайную строку) +# JWT секрет (сгенерируйте случайную строку: openssl rand -hex 32) CABINET_JWT_SECRET=your_random_secret_key_here -# Разрешенные origins для CORS -CABINET_ALLOWED_ORIGINS=http://localhost:3020,https://cabinet.yourdomain.com +# Разрешенные origins для CORS (домен, на котором будет кабинет) +CABINET_ALLOWED_ORIGINS=https://cabinet.example.com ``` -После изменений перезапустите бота. +Перезапустите бота после изменений. -## Настройка прокси для production +### Шаг 2. Получение frontend файлов -Frontend - это статические файлы (HTML, JS, CSS). Для работы нужно: - -1. Раздавать статику через веб-сервер -2. Проксировать `/api/*` запросы на backend бота - -> **💡 Важно:** Docker контейнер из этого репозитория содержит nginx, который слушает на **внутреннем порту 80**. -> Это НЕ хост-порт! Выберите один из вариантов ниже в зависимости от вашей инфраструктуры. - -### Вариант 1: Caddy раздает статику напрямую - -**✅ Рекомендуется** - без лишних слоев прокси, максимальная производительность. - -Соберите frontend и примонтируйте в Caddy: +#### Вариант A: Готовый Docker образ (рекомендуется) ```bash -# Соберите образ или скопируйте dist из контейнера -docker compose build -docker create --name temp_cabinet cabinet_frontend -docker cp temp_cabinet:/usr/share/nginx/html ./cabinet-dist -docker rm temp_cabinet +docker pull ghcr.io/bedolaga-dev/bedolaga-cabinet:latest ``` -Caddyfile: +Извлеките собранные файлы из образа: + +```bash +# Создать временный контейнер и скопировать статику +docker create --name tmp_cabinet ghcr.io/bedolaga-dev/bedolaga-cabinet:latest +docker cp tmp_cabinet:/usr/share/nginx/html ./cabinet-dist +docker rm tmp_cabinet +``` + +#### Вариант B: Сборка из исходников + +```bash +git clone https://github.com/BEDOLAGA-DEV/bedolaga-cabinet.git +cd bedolaga-cabinet +cp .env.example .env +``` + +Отредактируйте `.env`: + +```env +VITE_API_URL=/api +VITE_TELEGRAM_BOT_USERNAME=your_bot_username +VITE_APP_NAME=My VPN +VITE_APP_LOGO=V +``` + +Соберите и извлеките: + +```bash +docker compose build +docker create --name tmp_cabinet cabinet_frontend +docker cp tmp_cabinet:/usr/share/nginx/html ./cabinet-dist +docker rm tmp_cabinet +``` + +### Шаг 3. Размещение файлов на сервере + +Скопируйте содержимое `cabinet-dist` в директорию, которую будет раздавать ваш прокси: + +```bash +# Создайте директорию на сервере +sudo mkdir -p /srv/cabinet + +# Скопируйте файлы (с локальной машины или напрямую на сервере) +sudo cp -r ./cabinet-dist/* /srv/cabinet/ +``` + +### Шаг 4. Настройка обратного прокси + +#### Caddy (рекомендуется) + +Caddy автоматически получает и обновляет SSL сертификаты. ```caddyfile -cabinet.yourdomain.com { - root * /srv/cabinet - encode gzip +https://cabinet.example.com { + encode gzip zstd - # API запросы на backend + # API запросы → backend бота (удаляет /api префикс) handle /api/* { uri strip_prefix /api reverse_proxy remnawave_bot:8080 } - @websockets { - header_regexp Connection *Upgrade* - header Upgrade websocket - } - # WebSocket соединения - handle /cabinet/ws { - uri strip_prefix /api - reverse_proxy remnawave_bot:8080 { - transport http { - read_timeout 0 - write_timeout 0 - } - } - } - - # Статические файлы + # Frontend статика handle { + root * /srv/cabinet try_files {path} /index.html file_server - } -} -``` + # Кэширование статических ассетов (JS, CSS, шрифты, изображения) + @static path *.js *.css *.woff *.woff2 *.ttf *.ico *.png *.jpg *.jpeg *.svg *.webp *.gif + header @static Cache-Control "public, max-age=31536000, immutable" -docker-compose.yml для Caddy: - -```yaml -services: - caddy: - image: caddy:2-alpine - volumes: - - ./Caddyfile:/etc/caddy/Caddyfile:ro - - ./cabinet-dist:/srv/cabinet:ro - - caddy_data:/data - ports: - - '80:80' - - '443:443' - networks: - - bot_network -``` - -### Вариант 2: Проксирование на frontend контейнер - -Если хотите использовать готовый Docker контейнер с nginx внутри. - -**⚠️ Важно:** - -- Порт `80` в примерах - это **внутренний порт контейнера** (nginx внутри), не хост-порт! -- Контейнеры должны быть в одной Docker сети для связи друг с другом -- Имена контейнеров используются как DNS внутри Docker сети - -#### A. Если у вас УЖЕ запущен Caddy/Nginx в Docker: - -**Шаг 1:** Узнайте имя Docker сети вашего Caddy/Nginx: - -```bash -# Посмотреть сети -docker network ls - -# Или узнать сеть конкретного контейнера -docker inspect <имя_caddy_контейнера> | grep NetworkMode -``` - -**Шаг 2:** Создайте docker-compose.yml для frontend: - -```yaml -services: - cabinet-frontend: - image: ghcr.io/bedolaga-dev/bedolaga-cabinet:latest - container_name: cabinet_frontend - restart: unless-stopped - # НЕ открываем порты на хосте! Только внутри Docker сети - networks: - - bot_network - -networks: - bot_network: - external: true # Используем существующую сеть - name: remnawave-bedolaga-telegram-bot_bot_network # Пример для bedolaga bot -``` - -**Важно:** Замените имя сети на вашу: - -- Если у вас bot + caddy: используйте сеть бота (обычно `<название_проекта>_bot_network`) -- Если отдельный Caddy: узнайте через `docker network ls` -- Если используете Traefik: обычно `traefik` или `web` - -**Шаг 3:** Запустите frontend: - -```bash -docker compose up -d -``` - -**Шаг 4:** Добавьте в конфигурацию Caddy/Nginx: - -Caddy проксирует на контейнер: - -```caddyfile -cabinet.yourdomain.com { - # API на backend - handle /api/* { - uri strip_prefix /api - reverse_proxy remnawave_bot:8080 - } - - # Frontend контейнер (nginx внутри на порту 80) - handle { - reverse_proxy cabinet_frontend:80 + # HTML без кэша (для обновлений SPA) + @html path *.html / + header @html Cache-Control "no-cache, must-revalidate" } } ``` -Nginx (добавьте в существующий конфиг): +> **Примечание:** `remnawave_bot:8080` — имя контейнера бота в Docker сети. +> Если Caddy запущен на хосте, а не в Docker, используйте `localhost:8080` или IP сервера. + +#### Nginx ```nginx server { listen 443 ssl http2; - server_name cabinet.yourdomain.com; + server_name cabinet.example.com; - ssl_certificate /path/to/cert.pem; + ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; - # API на backend + root /srv/cabinet; + index index.html; + + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml image/svg+xml; + + # API запросы → backend бота location /api/ { rewrite ^/api/(.*) /$1 break; proxy_pass http://remnawave_bot:8080; @@ -268,28 +158,38 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # Frontend контейнер (nginx внутри на порту 80) + # Кэширование статических ассетов + location ~* \.(?:js|css|woff2?|ttf|ico|png|jpe?g|svg|webp|gif)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # SPA fallback location / { - proxy_pass http://cabinet_frontend:80; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; + try_files $uri /index.html; + add_header Cache-Control "no-cache, must-revalidate"; } } ``` -**Шаг 5:** Перезагрузите Caddy/Nginx: +### Шаг 5. Запуск и проверка ```bash -# Для Caddy +# Перезагрузите Caddy docker exec caddy reload --config /etc/caddy/Caddyfile -# Для Nginx +# Или Nginx docker exec nginx -s reload ``` -#### B. Если Caddy/Nginx ещё НЕ запущен: +Откройте `https://cabinet.example.com` в браузере. -docker-compose.yml: +## Альтернативная установка: Docker контейнер с nginx внутри + +Если вы не хотите раздавать статику напрямую, можно проксировать запросы на Docker контейнер cabinet. + +### docker-compose.yml ```yaml services: @@ -297,106 +197,136 @@ services: image: ghcr.io/bedolaga-dev/bedolaga-cabinet:latest container_name: cabinet_frontend restart: unless-stopped - # Можно открыть порт для прямого доступа (для тестирования) - # ports: - # - "3020:80" + # Порты НЕ пробрасываем — доступ только через Docker сеть networks: - - web + - bot_network networks: - web: - driver: bridge + bot_network: + external: true + name: remnawave-bedolaga-telegram-bot_bot_network # Сеть вашего бота ``` -Затем настройте Caddy/Nginx в той же сети `web`. +### Caddyfile + +```caddyfile +https://cabinet.example.com { + encode gzip zstd + + # API запросы → backend бота + handle /api/* { + uri strip_prefix /api + reverse_proxy remnawave_bot:8080 + } + + # Frontend → nginx контейнер (порт 80 внутри Docker сети) + handle { + reverse_proxy cabinet_frontend:80 + } +} +``` + +> В этом варианте кэшированием статики занимается nginx внутри контейнера. ## Переменные окружения -### Build-time (используются при сборке) +### Build-time (вшиваются в JS при сборке) -| Переменная | Описание | По умолчанию | -| ---------------------------- | ---------------------------------- | ------------ | -| `VITE_API_URL` | Путь к API (`/api` или полный URL) | `/api` | -| `VITE_TELEGRAM_BOT_USERNAME` | Username Telegram бота (без @) | - | -| `VITE_APP_NAME` | Название приложения | `Cabinet` | -| `VITE_APP_LOGO` | Логотип (короткий текст) | `V` | +| Переменная | Описание | По умолчанию | +|---|---|---| +| `VITE_API_URL` | Путь к API (`/api` или полный URL) | `/api` | +| `VITE_TELEGRAM_BOT_USERNAME` | Username Telegram бота (без @) | — | +| `VITE_APP_NAME` | Название в шапке и вкладке браузера | `Cabinet` | +| `VITE_APP_LOGO` | Текст логотипа (1-2 символа) | `V` | -### Runtime (только для Docker) +### Runtime (только для Docker контейнера) -| Переменная | Описание | По умолчанию | -| -------------- | --------------- | ------------ | -| `CABINET_PORT` | Порт контейнера | `3020` | +| Переменная | Описание | По умолчанию | +|---|---|---| +| `CABINET_PORT` | Порт контейнера на хосте | `3020` | + +### Backend бота (.env бота) + +| Переменная | Описание | По умолчанию | +|---|---|---| +| `CABINET_ENABLED` | Включить Cabinet API | `false` | +| `CABINET_JWT_SECRET` | Секретный ключ для JWT | `BOT_TOKEN` | +| `CABINET_ALLOWED_ORIGINS` | CORS origins (через запятую) | — | +| `CABINET_ACCESS_TOKEN_EXPIRE_MINUTES` | Время жизни access token | `15` | +| `CABINET_REFRESH_TOKEN_EXPIRE_DAYS` | Время жизни refresh token | `7` | + +## Устранение проблем + +### CORS ошибка в консоли браузера + +Домен кабинета не добавлен в `CABINET_ALLOWED_ORIGINS` в `.env` бота. Добавьте и перезапустите бота. + +### API возвращает HTML вместо JSON + +Прокси настроен неправильно — запросы `/api/*` попадают на frontend вместо backend. Проверьте порядок блоков `handle` в Caddyfile (API должен быть первым). + +### 502 Bad Gateway + +1. Backend бота не запущен — проверьте `docker ps` +2. Контейнеры в разных Docker сетях — проверьте и подключите: + ```bash + # Проверить сети контейнера + docker inspect --format='{{json .NetworkSettings.Networks}}' | python3 -m json.tool + + # Подключить к нужной сети + docker network connect + ``` +3. Неправильное имя сервиса в прокси — проверьте через: + ```bash + docker exec wget -qO- http://remnawave_bot:8080/health + ``` + +### Telegram авторизация не работает + +1. `VITE_TELEGRAM_BOT_USERNAME` должен быть без `@` +2. Домен кабинета добавлен в BotFather → Bot Settings → Domain + +### Белый экран / SPA не работает + +Прокси не настроен на fallback к `index.html`. Убедитесь что `try_files {path} /index.html` (Caddy) или `try_files $uri /index.html` (Nginx) присутствует в конфигурации. + +## Разработка + +```bash +git clone https://github.com/BEDOLAGA-DEV/bedolaga-cabinet.git +cd bedolaga-cabinet +npm install +cp .env.example .env +# Отредактируйте .env +npm run dev +``` + +Dev-сервер запустится на `http://localhost:5173` с автоматическим проксированием `/api` на `localhost:8080`. ## Структура проекта ``` bedolaga-cabinet/ ├── src/ -│ ├── api/ # API клиенты -│ ├── components/ # React компоненты -│ ├── contexts/ # React контексты +│ ├── api/ # API клиенты (axios) +│ ├── components/ # React компоненты (UI kit) +│ ├── contexts/ # React контексты (auth, theme) │ ├── hooks/ # Custom hooks │ ├── locales/ # Переводы (i18n) │ ├── pages/ # Страницы приложения │ ├── types/ # TypeScript типы │ └── utils/ # Утилиты ├── public/ # Статические файлы -├── Dockerfile # Docker образ -├── docker-compose.yml # Docker Compose конфигурация +├── Dockerfile # Multi-stage сборка (node → nginx) +├── docker-compose.yml # Docker Compose для сборки +├── nginx.conf # Nginx конфиг внутри контейнера └── .env.example # Пример переменных окружения ``` -## Устранение проблем - -### Ошибка CORS - -Убедитесь, что домен frontend добавлен в `CABINET_ALLOWED_ORIGINS` в настройках бота. - -### API возвращает HTML вместо JSON - -Проверьте настройку прокси - запросы на `/api/*` должны попадать на backend, а не на frontend. - -### 502 Bad Gateway - -Убедитесь что: - -1. Backend бот запущен и работает -2. Контейнеры находятся в одной Docker сети -3. Имя сервиса backend в прокси конфигурации правильное - -**Проверка связности контейнеров:** - -```bash -# Проверить что frontend доступен из Caddy/Nginx контейнера -docker exec wget -qO- http://cabinet_frontend:80 - -# Проверить что backend доступен -docker exec wget -qO- http://remnawave_bot:8080/health - -# Проверить в какой сети находятся контейнеры -docker inspect cabinet_frontend | grep -A 10 Networks -docker inspect | grep -A 10 Networks -``` - -Если контейнеры в разных сетях, подключите их: - -```bash -# Подключить frontend к сети Caddy -docker network connect cabinet_frontend - -# ИЛИ подключить Caddy к сети frontend -docker network connect -``` - -### Telegram авторизация не работает - -1. Проверьте `VITE_TELEGRAM_BOT_USERNAME` - должен быть без `@` -2. Убедитесь что домен добавлен в Bot Settings → Domain - ## Связанные проекты -- [Remnawave Bedolaga Telegram Bot](https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot) - Backend бота -- [Bedolaga Chat](https://t.me/+wTdMtSWq8YdmZmVi) - Чат поддержки +- [Remnawave Bedolaga Telegram Bot](https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot) — Backend бота +- [Bedolaga Chat](https://t.me/+wTdMtSWq8YdmZmVi) — Чат поддержки ## Контакты