# tgvpn **Версия:** [0.20.0](CHANGELOG.md) Telegram-бот на Go для управления VPN через панель [Remnawave](https://docs.rw/): проверка панели, создание пользователей, назначение сквадов. Данные хранятся в **PostgreSQL**. ## Содержание - [Требования](#требования) - [Быстрый старт](#быстрый-старт-docker-compose) - [PostgreSQL](#postgresql) - [Развёртывание на VPS](#развёртывание-на-vps-linux) - [Обновление бота](#обновление-бота) - [Переменные окружения](#переменные-окружения) - [Админ-меню](#админ-меню-в-боте) - [Устранение неполадок](#устранение-неполадок) ## Требования | Компонент | Минимум | |-----------|---------| | Docker | 24+ | | Docker Compose | v2 (`docker compose`) | | PostgreSQL | 16+ (в compose включён) | | Токен бота | [@BotFather](https://t.me/BotFather) | | Сеть | Исходящий HTTPS к `api.telegram.org` (порт 443) | Для запуска **без Docker**: Go 1.22+. --- ## Быстрый старт (Docker Compose) ### 1. Клонирование ```bash git clone cd tgvpn ``` ### 2. Переменные окружения ```bash cp .env.example .env ``` Отредактируйте `.env`: ```env BOT_TOKEN=ваш_токен_от_BotFather BOT_DEBUG=false TELEGRAM_ADMIN_ID=123456789 REMNAWAVE_PANEL_NAME=Панель 1 REMNAWAVE_PANEL_URL=https://panel.example.com REMNAWAVE_API_TOKEN=токен_из_панели REMNAWAVE_SUBSCRIPTION_URL=https://sub.example.com DATABASE_URL=postgres://tgvpn:tgvpn@db:5432/tgvpn?sslmode=disable DEFAULT_USER_DAYS=30 ``` > **Важно:** файл `.env` не попадает в git и не копируется в образ. Compose передаёт переменные в контейнер при старте. ### 3. Сборка и запуск Поднимаются два сервиса: **PostgreSQL** (`db`) и **бот** (`bot`). Бот стартует только после готовности БД. ```bash docker compose up -d --build ``` ### 4. Проверка ```bash # статус (db — healthy, bot — running) docker compose ps # логи бота (должно быть: «postgres ok», «бот @имя запущен») docker compose logs --tail=30 bot # логи PostgreSQL docker compose logs --tail=20 db ``` В Telegram: `/start`, от админа — `/admin squads`, `/admin user`. ### 5. Остановка ```bash # остановить контейнеры (данные БД сохраняются в volume pgdata) docker compose down # удалить и данные БД (осторожно!) docker compose down -v ``` --- ## PostgreSQL Бот **не работает без PostgreSQL**: при старте проверяется `DATABASE_URL`, применяется миграция, далее идёт работа с БД. ### Роль базы данных | Таблица | Назначение | |---------|------------| | `telegram_users` | Пользователи Telegram, зашедшие в бота (`/start`) | | `vpn_users` | Созданные в Remnawave аккаунты: UUID, логин, сквады, срок | | `admin_wizard` | Состояние мастера админа (создание пользователя, назначение сквадов) | Миграции лежат в `internal/db/migrations/` и применяются **автоматически** при каждом запуске бота (`CREATE TABLE IF NOT EXISTS`). ### Схема в Docker Compose ```yaml services: db: # PostgreSQL 16, volume pgdata bot: # ждёт healthy у db, затем стартует ``` Параметры по умолчанию (см. `docker-compose.yml`): | Параметр | Значение | |----------|----------| | Хост (внутри compose) | `db` | | Порт | `5432` | | База | `tgvpn` | | Пользователь | `tgvpn` | | Пароль | `tgvpn` | Строка подключения для бота: ```env DATABASE_URL=postgres://tgvpn:tgvpn@db:5432/tgvpn?sslmode=disable ``` Формат URL (общий вид): ``` postgres://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=disable ``` ### Подключение к БД вручную Из каталога проекта: ```bash # интерактивная консоль psql внутри контейнера docker compose exec db psql -U tgvpn -d tgvpn ``` Полезные запросы: ```sql -- все VPN-пользователи, созданные через бота SELECT remnawave_username, remnawave_uuid, expire_at, created_at FROM vpn_users; -- активные мастера админа SELECT admin_telegram_id, step, updated_at FROM admin_wizard; \q ``` ### Продакшен: смена пароля БД 1. Задайте сильный пароль в `docker-compose.yml` (секция `db.environment`) **или** вынесите в `.env`: ```yaml environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change_me_strong} ``` 2. Обновите `DATABASE_URL` в `.env` бота с тем же паролем. 3. Пересоздайте стек: ```bash docker compose down docker compose up -d --build ``` > Если меняете пароль у уже существующего volume `pgdata`, может понадобиться сброс volume или `ALTER USER` внутри старой БД. ### Внешний PostgreSQL (без контейнера `db`) Если БД на отдельном сервере или managed Postgres: 1. Создайте базу и пользователя с правами `CREATE`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`. 2. В `.env` укажите реальный URL: ```env DATABASE_URL=postgres://user:password@postgres.example.com:5432/tgvpn?sslmode=require ``` 3. В `docker-compose.yml` закомментируйте или удалите сервис `db` и `depends_on` у `bot`. 4. Убедитесь, что с хоста бота есть сетевой доступ к порту `5432`. ### Резервное копирование ```bash # дамп в файл (дата в имени) docker compose exec -T db pg_dump -U tgvpn tgvpn > backup_$(date +%Y%m%d).sql # восстановление (на пустую или новую БД) cat backup_20260101.sql | docker compose exec -T db psql -U tgvpn -d tgvpn ``` Рекомендуется настроить cron на VPS для ежедневных дампов. ### Проверка после деплоя ```bash docker compose ps # tgvpn-db running (healthy) # tgvpn-bot running docker compose logs bot | grep -i postgres # ожидается успешный старт без «ping postgres» / «apply migration» ошибок ``` ### Ошибки PostgreSQL | Симптом | Решение | |---------|---------| | `DATABASE_URL не задан` | Добавьте переменную в `.env` | | `connect postgres` / `ping postgres` | Проверьте, что `db` в состоянии `healthy`: `docker compose ps` | | Бот стартует раньше БД | В compose уже есть `depends_on: condition: service_healthy` — обновите compose | | `password authentication failed` | Совпадение пароля в `POSTGRES_PASSWORD` и в `DATABASE_URL` | | Пустые таблицы после создания user | Смотрите логи бота и ответ Remnawave API; запись в `vpn_users` идёт после успешного `POST /api/users` | --- ## Развёртывание на VPS (Linux) Ниже — пошаговая установка на чистый сервер (Ubuntu 22.04/24.04, Debian 12). Аналогично на других дистрибутивах с Docker. ### Шаг 1. Подключение к серверу ```bash ssh user@your-server-ip ``` ### Шаг 2. Установка Docker ```bash sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "${VERSION_CODENAME}") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo usermod -aG docker $USER ``` Выйдите из SSH и зайдите снова, чтобы группа `docker` применилась: ```bash exit ssh user@your-server-ip docker --version docker compose version ``` ### Шаг 3. Клонирование проекта ```bash sudo mkdir -p /opt/tgvpn sudo chown $USER:$USER /opt/tgvpn cd /opt/tgvpn git clone . ``` ### Шаг 4. Настройка `.env` ```bash cp .env.example .env nano .env # или vim / vi ``` Укажите реальный `BOT_TOKEN`, `DATABASE_URL` (в compose для VPS обычно оставляют `postgres://tgvpn:...@db:5432/...`), Remnawave и при необходимости UUID сквадов (`DEFAULT_*`). Для продакшена смените пароль PostgreSQL — см. [PostgreSQL → Продакшен](#продакшен-смена-пароля-бд). Права на секреты: ```bash chmod 600 .env ``` ### Шаг 5. Запуск в фоне ```bash docker compose up -d --build ``` Проверка: ```bash docker compose ps docker compose logs --tail=50 bot docker compose logs --tail=20 db ``` ### Шаг 6. Автозапуск после перезагрузки сервера В `docker-compose.yml` уже указано `restart: unless-stopped`. После перезагрузки VPS контейнер поднимется сам, если Docker запущен: ```bash sudo systemctl enable docker sudo systemctl start docker ``` ### Шаг 7. Обновление См. раздел [Обновление бота](#обновление-бота) ниже. --- ## Обновление бота Инструкция для уже работающей установки: подтянуть код из git, пересобрать контейнер и убедиться, что бот запустился. ### Перед обновлением 1. Убедитесь, что `.env` сохранён на сервере (он не в git). 2. Посмотрите, не появились ли новые переменные в `.env.example`: ```bash cd /opt/tgvpn # или каталог, куда клонировали проект git fetch origin git diff HEAD origin/main -- .env.example ``` Если в `.env.example` есть новые строки — добавьте их в свой `.env` вручную. ### Обновление на VPS (Docker) ```bash cd /opt/tgvpn # 1. Скачать изменения git pull origin main # 2. Пересобрать и перезапустить (простой даунтайм ~10–30 сек) docker compose up -d --build # 3. Проверить логи docker compose ps docker compose logs --tail=50 bot ``` В логах должны быть строки вида: `бот @имя_бота запущен` и `администратор ID …`. Проверка в Telegram (от аккаунта админа): - `/start` — бот отвечает - `/admin check` — проверка панели Remnawave ### Обновление на Windows (Docker Desktop) ```powershell cd tgvpn git pull origin main docker compose up -d --build docker compose logs --tail=50 bot ``` ### Обновление без Docker (локально) ```bash cd tgvpn git pull origin main go build -o bot . # остановите старый процесс бота, затем: ./bot ``` ### Если изменили только `.env` Пересборка не нужна — достаточно пересоздать контейнер: ```bash docker compose up -d --force-recreate ``` ### Откат на предыдущую версию ```bash cd /opt/tgvpn git log --oneline -5 # найти нужный коммит git checkout <хеш_коммита> # например: git checkout f360d53 docker compose up -d --build ``` Вернуться на актуальную ветку: ```bash git checkout main git pull origin main docker compose up -d --build ``` ### Очистка старых образов Docker (опционально) После нескольких обновлений на диске копятся неиспользуемые слои: ```bash docker image prune -f ``` ### Частые проблемы при обновлении | Симптом | Решение | |---------|---------| | `git pull` конфликтует с локальными правками | `git stash` → `git pull` → `git stash pop` или сбросить локальные изменения: `git checkout -- .` | | Бот не стартует после pull | `docker compose logs bot` — часто не хватает новой переменной в `.env` | | Старый код в контейнере | Обязательно `--build`: `docker compose up -d --build` | | Нет доступа к git | Проверьте SSH/HTTPS-доступ к вашему git-серверу | --- ## Развёртывание на Windows ### Docker Desktop 1. Установите [Docker Desktop](https://www.docker.com/products/docker-desktop/). 2. В PowerShell: ```powershell git clone cd tgvpn Copy-Item .env.example .env # отредактируйте .env — вставьте BOT_TOKEN docker compose up -d --build docker compose logs -f bot ``` --- ## Локальная разработка (без Docker) Нужен запущенный PostgreSQL 16+ (локально или только контейнер БД): ```bash # вариант: только БД в Docker, бот на хосте docker compose up -d db ``` В `.env` для локального бота укажите: ```env DATABASE_URL=postgres://tgvpn:tgvpn@localhost:5432/tgvpn?sslmode=disable ``` Проброс порта в `docker-compose.yml` (если ещё нет): ```yaml db: ports: - "5432:5432" ``` Запуск: ```bash cp .env.example .env go run . ``` или ```bash go build -o bot . ./bot ``` --- ## Переменные окружения | Переменная | Обязательно | Описание | |--------------|-------------|----------| | `BOT_TOKEN` | да | Токен от @BotFather | | `TELEGRAM_ADMIN_ID` | да | Числовой Telegram user ID администратора (например, [@userinfobot](https://t.me/userinfobot)) | | `REMNAWAVE_PANEL_NAME` | нет | Название панели в админ-меню (по умолчанию «Панель 1») | | `REMNAWAVE_PANEL_URL` | да | URL панели — сюда же идут запросы API (`/api/...`). Пример: `https://panel.example.com` ([док](https://docs.rw/docs/install/subscription-page/bundled)) | | `REMNAWAVE_API_TOKEN` | да | Токен из **Remnawave Settings → API Tokens**, заголовок `Authorization: Bearer` | | `CADDY_AUTH_API_TOKEN` | нет | `X-Api-Key`, если включён Caddy with security (как в оф. `.env` subscription-page) | | `REMNAWAVE_SUBSCRIPTION_URL` | нет | Опционально: домен Subscription Page (`sub.*`), отдельная проверка | | `DATABASE_URL` | да | PostgreSQL, в compose: `postgres://tgvpn:tgvpn@db:5432/tgvpn?sslmode=disable` | | `DEFAULT_USER_DAYS` | нет | Срок подписки по умолчанию (30) | | `DEFAULT_EXTERNAL_SQUAD_UUID` | нет | External squad по умолчанию при быстром создании | | `DEFAULT_INTERNAL_SQUAD_UUIDS` | нет | Internal squads через запятую | | `BOT_DEBUG` | нет | `true` — подробные логи Telegram API (только для отладки) | ### Админ-меню в боте Только пользователь с `TELEGRAM_ADMIN_ID`: - `/admin` — админ-меню (панель 1, Remnawave) - `/admin check` — полная проверка: веб панели, API (статистика, users, nodes), подписка (settings + API), страница подписки - `/admin config` — конфиг панели в боте - `/admin user` — мастер создания пользователя в Remnawave + назначение сквадов - `/admin user <логин> [дней]` — быстрое создание (сквады из `DEFAULT_*` в `.env`) - `/admin squads` — список internal/external squads - `/admin assign <логин>` — назначить сквады существующему пользователю - Кнопки: «Создать пользователя», «Сквады», «Проверить панель», «Конфиг» --- ## Remnawave API (по официальной документации) Как в [Bundled Subscription Page](https://docs.rw/docs/install/subscription-page/bundled): ```env REMNAWAVE_PANEL_URL=https://panel.example.com REMNAWAVE_API_TOKEN=API_TOKEN_FROM_REMNAWAVE CADDY_AUTH_API_TOKEN= ``` - **Отдельного `REMNAWAVE_API_URL` нет** — API всегда на том же хосте, что и панель: `{REMNAWAVE_PANEL_URL}/api/...` - Авторизация: `Authorization: Bearer {REMNAWAVE_API_TOKEN}` - Внутри Docker-сети Remnawave: `REMNAWAVE_PANEL_URL=http://remnawave:3000` - Домен `sub.*` — это Subscription Page, не панель; для API используйте `panel.*` Пример проверки с сервера: ```bash curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $REMNAWAVE_API_TOKEN" \ "$REMNAWAVE_PANEL_URL/api/system/stats/recap" ``` --- ## Полезные команды Docker ```bash # пересобрать образ и перезапустить docker compose up -d --build # логи в реальном времени docker compose logs -f bot # последние 100 строк логов docker compose logs --tail=100 bot # зайти в контейнер бота docker compose exec bot sh # консоль PostgreSQL docker compose exec db psql -U tgvpn -d tgvpn # дамп базы docker compose exec -T db pg_dump -U tgvpn tgvpn > backup.sql # удалить контейнеры (данные БД в volume сохранятся) docker compose down # удалить контейнер и неиспользуемые образы проекта docker compose down --rmi local ``` --- ## Сеть и безопасность - Бот использует **long polling**: входящие запросы на ваш сервер **не нужны**, порты открывать не требуется. - Нужен только **исходящий** доступ к `https://api.telegram.org`. - Не коммитьте `.env` в git. Не публикуйте `BOT_TOKEN` и пароль БД. - PostgreSQL доступен **только внутри docker-сети** (порт наружу не проброшен по умолчанию). - Контейнер бота запускается от непривилегированного пользователя `bot` (UID 10001). Если позже добавите **webhook**, понадобится reverse proxy (nginx/Caddy), TLS и открытый порт 443 — это описывается отдельно при появлении функции. --- ## Устранение неполадок ### `BOT_TOKEN не задан` / `TELEGRAM_ADMIN_ID не задан` - Проверьте, что файл `.env` лежит рядом с `docker-compose.yml`. - В `.env` нет пробелов вокруг `=`: `BOT_TOKEN=123:ABC`, не `BOT_TOKEN = ...`. - `TELEGRAM_ADMIN_ID` — только цифры, без `@username`. - После правки: `docker compose up -d --force-recreate`. ### `Authentication failed` / `401 Unauthorized` - Неверный или отозванный токен. Создайте новый в @BotFather → `/token` → обновите `.env` → `docker compose up -d --force-recreate`. ### Бот не отвечает в Telegram ```bash docker compose ps # State должен быть running docker compose logs bot # ошибки сети, токена ``` - Убедитесь, что на сервере нет блокировки Telegram (firewall, провайдер). - Проверьте: `curl -I https://api.telegram.org` с хоста. ### API возвращает 502, веб-панель — 200 Частая причина: в `REMNAWAVE_PANEL_URL` указан домен **страницы подписки** (`sub.example.com`), а не **админ-панели** (`panel.example.com`). 1. Укажите URL **панели** (не sub): `REMNAWAVE_PANEL_URL=https://panel.example.com` 2. Токен API: `REMNAWAVE_API_TOKEN=...` (Settings → API Tokens) 3. Страницу подписки — опционально: `REMNAWAVE_SUBSCRIPTION_URL=https://sub.example.com` 4. Проверьте на сервере: `docker compose ps` (Remnawave Panel запущен), логи reverse proxy ### Контейнер постоянно перезапускается ```bash docker compose logs --tail=200 bot ``` Чаще всего — пустой `BOT_TOKEN` или ошибка при старте. ### `DATABASE_URL не задан` / ошибки PostgreSQL См. раздел [PostgreSQL → Ошибки](#ошибки-postgresql). ### Нет доступа к `docker` без sudo ```bash sudo usermod -aG docker $USER # перелогиньтесь ``` --- ## Структура проекта ``` tgvpn/ ├── main.go ├── internal/ │ ├── bot/ # Telegram, админ-меню, создание пользователей │ ├── config/ # переменные окружения │ ├── db/ # PostgreSQL: подключение, миграции, репозитории │ │ └── migrations/ # SQL-миграции (001_init.sql) │ └── remnawave/ # API панели (users, squads) ├── Dockerfile # multi-stage сборка ├── docker-compose.yml # bot + PostgreSQL (volume pgdata) ├── .env.example # шаблон переменных ├── .dockerignore ├── go.mod / go.sum ├── CHANGELOG.md └── README.md ``` --- ## Репозиторий Укажите URL вашего приватного git-репозитория при клонировании.