Веб-интерфейс личного кабинета для VPN бота на базе Remnawave Bedolaga Telegram Bot V3.0.0+.
React + Vite + TypeScript | Авторизация через Telegram | Мультиязычность (EN/RU) | Адаптивный дизайн
- Docker и Docker Compose
- Запущенный backend бота с включенным Cabinet API
- Обратный прокси (Caddy / Nginx / Traefik)
Браузер → Caddy/Nginx → /api/* → remnawave_bot:8080 (backend API)
→ /* → /srv/cabinet (статика frontend)
Frontend — это статические файлы (HTML, JS, CSS). Обратный прокси выполняет две задачи:
- Раздает статику frontend
- Проксирует
/api/*запросы на backend бота (с удалением префикса/api)
В .env файле бота добавьте:
# Включить Cabinet API
CABINET_ENABLED=true
# JWT секрет (сгенерируйте случайную строку: openssl rand -hex 32)
CABINET_JWT_SECRET=your_random_secret_key_here
# Разрешенные origins для CORS (домен, на котором будет кабинет)
CABINET_ALLOWED_ORIGINS=https://cabinet.example.comПерезапустите бота после изменений.
docker pull ghcr.io/bedolaga-dev/bedolaga-cabinet:latestИзвлеките собранные файлы из образа:
# Создать временный контейнер и скопировать статику
docker create --name tmp_cabinet ghcr.io/bedolaga-dev/bedolaga-cabinet:latest
mkdir -p ./cabinet-dist
docker cp tmp_cabinet:/usr/share/nginx/html/. ./cabinet-dist/
docker rm tmp_cabinetgit clone https://github.com/BEDOLAGA-DEV/bedolaga-cabinet.git
cd bedolaga-cabinet
cp .env.example .envОтредактируйте .env:
VITE_API_URL=/api
VITE_TELEGRAM_BOT_USERNAME=your_bot_username
VITE_APP_NAME=My VPN
VITE_APP_LOGO=VСоберите и извлеките:
docker compose build cabinet-frontend
docker compose create cabinet-frontend
mkdir -p ./cabinet-dist
docker cp cabinet_frontend:/usr/share/nginx/html/. ./cabinet-dist/
docker compose rm -sf cabinet-frontendСкопируйте содержимое cabinet-dist в директорию, которую будет раздавать ваш прокси:
# Создайте директорию на сервере
sudo mkdir -p /srv/cabinet
# Скопируйте файлы (с локальной машины или напрямую на сервере)
sudo cp -r ./cabinet-dist/* /srv/cabinet/Caddy автоматически получает и обновляет SSL сертификаты.
https://cabinet.example.com {
encode gzip zstd
# API запросы → backend бота (удаляет /api префикс)
handle /api/* {
uri strip_prefix /api
reverse_proxy remnawave_bot:8080
}
# 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"
# HTML без кэша (для обновлений SPA)
@html path *.html /
header @html Cache-Control "no-cache, must-revalidate"
}
}Примечание:
remnawave_bot:8080— имя контейнера бота в Docker сети. Если Caddy запущен на хосте, а не в Docker, используйтеlocalhost:8080или IP сервера.
server {
listen 443 ssl http2;
server_name cabinet.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
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;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Кэширование статических ассетов
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 / {
try_files $uri /index.html;
add_header Cache-Control "no-cache, must-revalidate";
}
}# Перезагрузите Caddy
docker exec <caddy_container> caddy reload --config /etc/caddy/Caddyfile
# Или Nginx
docker exec <nginx_container> nginx -s reloadОткройте https://cabinet.example.com в браузере.
Если вы не хотите раздавать статику напрямую, можно проксировать запросы на Docker контейнер cabinet.
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 # Сеть вашего бота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 внутри контейнера.
| Переменная | Описание | По умолчанию |
|---|---|---|
VITE_API_URL |
Путь к API (/api или полный URL) |
/api |
VITE_TELEGRAM_BOT_USERNAME |
Username Telegram бота (без @) | — |
VITE_APP_NAME |
Название в шапке и вкладке браузера | Cabinet |
VITE_APP_LOGO |
Текст логотипа (1-2 символа) | V |
| Переменная | Описание | По умолчанию |
|---|---|---|
CABINET_PORT |
Порт контейнера на хосте | 3020 |
| Переменная | Описание | По умолчанию |
|---|---|---|
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 |
Домен кабинета не добавлен в CABINET_ALLOWED_ORIGINS в .env бота. Добавьте и перезапустите бота.
Прокси настроен неправильно — запросы /api/* попадают на frontend вместо backend. Проверьте порядок блоков handle в Caddyfile (API должен быть первым).
- Backend бота не запущен — проверьте
docker ps - Контейнеры в разных Docker сетях — проверьте и подключите:
# Проверить сети контейнера docker inspect <container> --format='{{json .NetworkSettings.Networks}}' | python3 -m json.tool # Подключить к нужной сети docker network connect <network_name> <container_name>
- Неправильное имя сервиса в прокси — проверьте через:
docker exec <caddy_container> wget -qO- http://remnawave_bot:8080/health
VITE_TELEGRAM_BOT_USERNAMEдолжен быть без@- Домен кабинета добавлен в BotFather → Bot Settings → Domain
Прокси не настроен на fallback к index.html. Убедитесь что try_files {path} /index.html (Caddy) или try_files $uri /index.html (Nginx) присутствует в конфигурации.
git clone https://github.com/BEDOLAGA-DEV/bedolaga-cabinet.git
cd bedolaga-cabinet
npm install
cp .env.example .env
# Отредактируйте .env
npm run devDev-сервер запустится на http://localhost:5173 с автоматическим проксированием /api на localhost:8080.
bedolaga-cabinet/
├── src/
│ ├── api/ # API клиенты (axios)
│ ├── components/ # React компоненты (UI kit)
│ ├── contexts/ # React контексты (auth, theme)
│ ├── hooks/ # Custom hooks
│ ├── locales/ # Переводы (i18n)
│ ├── pages/ # Страницы приложения
│ ├── types/ # TypeScript типы
│ └── utils/ # Утилиты
├── public/ # Статические файлы
├── Dockerfile # Multi-stage сборка (node → nginx)
├── docker-compose.yml # Docker Compose для сборки
├── nginx.conf # Nginx конфиг внутри контейнера
└── .env.example # Пример переменных окружения
- Remnawave Bedolaga Telegram Bot — Backend бота
- Bedolaga Chat — Чат поддержки
- Telegram: @fringg
- Telegram: @pedzeo
- Чат: Bedolaga Chat