diff --git a/README.md b/README.md index 894a77b..c53f094 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,18 @@ **Версия:** [0.20.0](CHANGELOG.md) -Telegram-бот на Go (базовое приветствие; далее — VPN-функции). +Telegram-бот на Go для управления VPN через панель [Remnawave](https://docs.rw/): проверка панели, создание пользователей, назначение сквадов. Данные хранятся в **PostgreSQL**. + +## Содержание + +- [Требования](#требования) +- [Быстрый старт](#быстрый-старт-docker-compose) +- [PostgreSQL](#postgresql) +- [Развёртывание на VPS](#развёртывание-на-vps-linux) +- [Обновление бота](#обновление-бота) +- [Переменные окружения](#переменные-окружения) +- [Админ-меню](#админ-меню-в-боте) +- [Устранение неполадок](#устранение-неполадок) ## Требования @@ -43,12 +54,16 @@ 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 ``` @@ -56,23 +71,164 @@ docker compose up -d --build ### 4. Проверка ```bash -# логи (должно быть: «бот @имя_бота запущен») -docker compose logs -f bot - -# статус контейнера +# статус (db — healthy, bot — running) docker compose ps + +# логи бота (должно быть: «postgres ok», «бот @имя запущен») +docker compose logs --tail=30 bot + +# логи PostgreSQL +docker compose logs --tail=20 db ``` -В Telegram откройте бота и отправьте `/start`. +В 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. @@ -126,7 +282,9 @@ cp .env.example .env nano .env # или vim / vi ``` -Укажите реальный `BOT_TOKEN`. Для продакшена оставьте `BOT_DEBUG=false`. +Укажите реальный `BOT_TOKEN`, `DATABASE_URL` (в compose для VPS обычно оставляют `postgres://tgvpn:...@db:5432/...`), Remnawave и при необходимости UUID сквадов (`DEFAULT_*`). + +Для продакшена смените пароль PostgreSQL — см. [PostgreSQL → Продакшен](#продакшен-смена-пароля-бд). Права на секреты: @@ -145,6 +303,7 @@ docker compose up -d --build ```bash docker compose ps docker compose logs --tail=50 bot +docker compose logs --tail=20 db ``` ### Шаг 6. Автозапуск после перезагрузки сервера @@ -285,9 +444,31 @@ 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 -# укажите BOT_TOKEN в .env go run . ``` @@ -369,10 +550,16 @@ 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 # удалить контейнер и неиспользуемые образы проекта @@ -385,8 +572,9 @@ docker compose down --rmi local - Бот использует **long polling**: входящие запросы на ваш сервер **не нужны**, порты открывать не требуется. - Нужен только **исходящий** доступ к `https://api.telegram.org`. -- Не коммитьте `.env` в git. Не публикуйте `BOT_TOKEN`. -- Контейнер запускается от непривилегированного пользователя `bot` (UID 10001). +- Не коммитьте `.env` в git. Не публикуйте `BOT_TOKEN` и пароль БД. +- PostgreSQL доступен **только внутри docker-сети** (порт наружу не проброшен по умолчанию). +- Контейнер бота запускается от непривилегированного пользователя `bot` (UID 10001). Если позже добавите **webhook**, понадобится reverse proxy (nginx/Caddy), TLS и открытый порт 443 — это описывается отдельно при появлении функции. @@ -432,6 +620,10 @@ docker compose logs --tail=200 bot Чаще всего — пустой `BOT_TOKEN` или ошибка при старте. +### `DATABASE_URL не задан` / ошибки PostgreSQL + +См. раздел [PostgreSQL → Ошибки](#ошибки-postgresql). + ### Нет доступа к `docker` без sudo ```bash @@ -449,10 +641,11 @@ tgvpn/ ├── internal/ │ ├── bot/ # Telegram, админ-меню, создание пользователей │ ├── config/ # переменные окружения -│ ├── db/ # PostgreSQL, миграции, мастер админа +│ ├── db/ # PostgreSQL: подключение, миграции, репозитории +│ │ └── migrations/ # SQL-миграции (001_init.sql) │ └── remnawave/ # API панели (users, squads) ├── Dockerfile # multi-stage сборка -├── docker-compose.yml # оркестрация +├── docker-compose.yml # bot + PostgreSQL (volume pgdata) ├── .env.example # шаблон переменных ├── .dockerignore ├── go.mod / go.sum