Run the panel as its own compose project behind the host Caddy
The instance's internal Caddy is only for Stoat itself, so the panel no longer goes through it: the bot ships its own compose project, publishes 3005 on loopback, and the host's external Caddy gives it a domain. extra_hosts pins the instance domain to host-gateway, so the bot reaches the API, gateway and LiveKit through the external proxy with a valid certificate instead of depending on router NAT loopback. The old in-project layout stays available as a fallback example, together with a step-by-step guide for when voice fails to connect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a9b7ccdd16
commit
62d5291cd9
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
# ---------------------------------------------------------------- Stoat ----
|
# ---------------------------------------------------------------- Stoat ----
|
||||||
# Публичный адрес API вашего инстанса (тот же, что в клиенте).
|
# Публичный адрес API вашего инстанса (тот же, что в клиенте).
|
||||||
# Внутри docker-сети Stoat можно использовать http://api:14702 — но тогда
|
# Бот ходит по нему как обычный клиент; домен резолвится в хост через
|
||||||
# убедитесь, что LiveKit URL из join_call резолвится изнутри контейнера.
|
# extra_hosts в compose.yml, поэтому TLS валидный и NAT loopback не нужен.
|
||||||
STOAT_API_URL=https://stoat.example.com/api
|
STOAT_API_URL=https://chat.example.com/api
|
||||||
|
|
||||||
# Токен бота: Settings → My Bots → создать бота → скопировать токен.
|
# Токен бота: Settings → My Bots → создать бота → скопировать токен.
|
||||||
STOAT_BOT_TOKEN=
|
STOAT_BOT_TOKEN=
|
||||||
|
|||||||
@@ -67,51 +67,39 @@ PUBLIC_URL=https://music.example.com
|
|||||||
JWT_SECRET=<случайные 32 байта>
|
JWT_SECRET=<случайные 32 байта>
|
||||||
```
|
```
|
||||||
|
|
||||||
`STOAT_API_URL` лучше указывать публичный (`https://домен/api`): бот тогда ведёт себя как обычный
|
`STOAT_API_URL` указывается публичный (`https://домен/api`): бот ведёт себя как обычный клиент —
|
||||||
клиент и получает от `join_call` тот же LiveKit-URL, что и все. Внутренний `http://api:14702` тоже
|
ходит в API, gateway и LiveKit через тот же внешний прокси с валидным TLS, что и браузеры.
|
||||||
работает, но убедитесь, что LiveKit-URL из ответа резолвится изнутри контейнера.
|
|
||||||
|
|
||||||
### 4. Добавить сервис в `compose.override.yml`
|
### 4. Указать домен инстанса в `compose.yml`
|
||||||
|
|
||||||
В `/opt/stoat/compose.override.yml` (готовый пример — [deploy/compose.override.yml.example](deploy/compose.override.yml.example)):
|
Бот запускается **отдельным compose-проектом** и с внутренним Caddy инстанса никак не связан.
|
||||||
|
В [compose.yml](compose.yml) поправьте одну строку — домен вашего Stoat:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
extra_hosts:
|
||||||
caddy:
|
- "chat.example.com:host-gateway"
|
||||||
ports: !override
|
|
||||||
- "127.0.0.1:8880:80"
|
|
||||||
|
|
||||||
mbot:
|
|
||||||
build: ../stoat-mbot
|
|
||||||
restart: always
|
|
||||||
env_file: ../stoat-mbot/.env
|
|
||||||
depends_on:
|
|
||||||
api:
|
|
||||||
condition: service_started
|
|
||||||
volumes:
|
|
||||||
- ../stoat-mbot/data:/data
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Сервис попадает в тот же compose-проект и ту же сеть, поэтому Caddy видит его как `http://mbot:3005`,
|
Эта запись заставляет контейнер резолвить домен инстанса в сам хост, где внешний Caddy держит
|
||||||
а бот ходит в Stoat API по имени `api`.
|
443 с валидным сертификатом. Так бот не зависит от NAT loopback на роутере.
|
||||||
|
|
||||||
### 5. Пробросить панель через Caddy
|
### 5. Повесить панель на внешний Caddy
|
||||||
|
|
||||||
В `/opt/stoat/Caddyfile` добавьте блок ([deploy/Caddyfile.snippet](deploy/Caddyfile.snippet)):
|
Порт панели публикуется только на локальный интерфейс (`127.0.0.1:3005`), домен выдаёт внешний
|
||||||
|
Caddy хоста — в `/etc/caddy/Caddyfile` ([deploy/Caddyfile.snippet](deploy/Caddyfile.snippet)):
|
||||||
|
|
||||||
```caddyfile
|
```caddyfile
|
||||||
http://music.example.com {
|
music.example.com {
|
||||||
reverse_proxy http://mbot:3005
|
reverse_proxy 127.0.0.1:3005
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Схема `http://` — потому что у вас TLS терминирует внешний прокси (Caddy слушает `127.0.0.1:8880`).
|
Затем `sudo systemctl reload caddy`. WebSocket проксируется автоматически.
|
||||||
WebSocket проксируется автоматически.
|
|
||||||
|
|
||||||
### 6. Запустить
|
### 6. Запустить
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/stoat && docker compose up -d --build mbot && docker compose logs -f mbot
|
cd /opt/stoat-mbot && docker compose up -d --build && docker compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
В логах должно появиться `yt-dlp detected`, `bot is ready` и `panel is listening`.
|
В логах должно появиться `yt-dlp detected`, `bot is ready` и `panel is listening`.
|
||||||
@@ -142,6 +130,21 @@ cd /opt/stoat && docker compose up -d --build mbot && docker compose logs -f mbo
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Если бот не заходит в голосовой канал
|
||||||
|
|
||||||
|
Порядок диагностики — сверху вниз, каждый шаг отсекает свой слой:
|
||||||
|
|
||||||
|
1. **`bot is ready` в логах.** Нет — проблема в `STOAT_API_URL`/`STOAT_BOT_TOKEN`. Проверьте, что
|
||||||
|
домен из `extra_hosts` совпадает с доменом в `STOAT_API_URL`, и что изнутри контейнера он
|
||||||
|
резолвится в хост: `docker compose exec mbot node -e "fetch(process.env.STOAT_API_URL).then(r=>console.log(r.status))"`.
|
||||||
|
2. **`joining voice channel`, но нет `voice connection established`.** Значит `join_call` отдал
|
||||||
|
токен, а WebSocket до LiveKit не поднялся — смотрите, доступен ли `/livekit` через внешний домен.
|
||||||
|
3. **Бот в канале, но звука нет.** Это уже медиа-трафик: LiveKit анонсирует клиентам свой адрес
|
||||||
|
из `rtc.node_ip` / `use_external_ip` в `/opt/stoat/livekit.yml` и ждёт UDP на 50000-50100.
|
||||||
|
Если анонсируется внешний IP, а роутер не умеет NAT loopback, пакеты от контейнера до него не
|
||||||
|
дойдут. Тогда либо включите hairpin на роутере, либо запустите бота внутри compose-проекта
|
||||||
|
Stoat — пример в [deploy/compose.stoat-network.yml.example](deploy/compose.stoat-network.yml.example).
|
||||||
|
|
||||||
## Настройки
|
## Настройки
|
||||||
|
|
||||||
Все параметры — в [.env.example](.env.example). Что стоит знать:
|
Все параметры — в [.env.example](.env.example). Что стоит знать:
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
name: stoat-mbot
|
||||||
|
|
||||||
|
services:
|
||||||
|
mbot:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
ports:
|
||||||
|
# Наружу не торчит: домен вешается на внешний Caddy хоста.
|
||||||
|
- "127.0.0.1:3005:3005"
|
||||||
|
extra_hosts:
|
||||||
|
# Домен Stoat резолвится в хост, где внешний Caddy держит валидный TLS.
|
||||||
|
# Так бот ходит в API, gateway и LiveKit ровно как обычный клиент,
|
||||||
|
# без зависимости от NAT loopback на роутере.
|
||||||
|
# Замените на свой домен:
|
||||||
|
- "chat.example.com:host-gateway"
|
||||||
|
volumes:
|
||||||
|
# cookies.txt для yt-dlp и/или локальная медиатека
|
||||||
|
- ./data:/data
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
# Добавьте отдельным блоком в /opt/stoat/Caddyfile, рядом с {$HOSTNAME} { ... }.
|
# Внешний Caddy на хосте: /etc/caddy/Caddyfile
|
||||||
#
|
# Добавьте блок рядом с остальными вашими сайтами.
|
||||||
# У вас Caddy слушает только 80 (127.0.0.1:8880 → 80), а TLS терминирует
|
music.example.com {
|
||||||
# внешний прокси на хосте, поэтому используем схему http:// —
|
reverse_proxy 127.0.0.1:3005
|
||||||
# так Caddy не будет пытаться сам выпускать сертификат.
|
|
||||||
http://music.example.com {
|
|
||||||
reverse_proxy http://mbot:3005
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Если внешнего прокси нет и Caddy сам держит 80/443 — просто:
|
# Внутренний Caddy инстанса трогать не нужно: панель к нему не относится.
|
||||||
# music.example.com {
|
# WebSocket (/ws) Caddy апгрейдит сам, отдельных директив не требуется.
|
||||||
# reverse_proxy http://mbot:3005
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# WebSocket (/ws) проксируется автоматически, отдельных директив не нужно.
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# /opt/stoat/compose.override.yml
|
|
||||||
#
|
|
||||||
# Раскладка:
|
|
||||||
# /opt/stoat — ваш self-hosted Stoat (compose.yml, Caddyfile, ...)
|
|
||||||
# /opt/stoat-mbot — этот репозиторий
|
|
||||||
#
|
|
||||||
# Сервис живёт в том же compose-проекте, поэтому попадает в сеть stoat_default:
|
|
||||||
# Caddy видит его как http://mbot:3005, а бот — Stoat API как http://api:14702.
|
|
||||||
services:
|
|
||||||
# ваш существующий override для Caddy — оставьте как есть
|
|
||||||
caddy:
|
|
||||||
ports: !override
|
|
||||||
- "127.0.0.1:8880:80"
|
|
||||||
|
|
||||||
mbot:
|
|
||||||
build: ../stoat-mbot
|
|
||||||
restart: always
|
|
||||||
env_file: ../stoat-mbot/.env
|
|
||||||
depends_on:
|
|
||||||
api:
|
|
||||||
condition: service_started
|
|
||||||
volumes:
|
|
||||||
# cookies.txt для yt-dlp и/или локальная медиатека
|
|
||||||
- ../stoat-mbot/data:/data
|
|
||||||
# Панель отдаётся через Caddy. Порт наружу нужен, только если панель
|
|
||||||
# проксирует внешний nginx на хосте, минуя Caddy:
|
|
||||||
# ports:
|
|
||||||
# - "127.0.0.1:3005:3005"
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Запасной вариант: если по какой-то причине бот должен ходить в Stoat
|
||||||
|
# по внутренним именам сервисов, а не через внешний домен.
|
||||||
|
#
|
||||||
|
# Кладётся в /opt/stoat/compose.override.yml (в дополнение к вашему caddy-блоку),
|
||||||
|
# при этом в /opt/stoat-mbot/.env нужно указать STOAT_API_URL=http://api:14702.
|
||||||
|
#
|
||||||
|
# Минус: LiveKit-URL из join_call всё равно приходит публичным, и его придётся
|
||||||
|
# резолвить изнутри сети — поэтому по умолчанию рекомендуется отдельный
|
||||||
|
# compose-проект (см. compose.yml в корне репозитория).
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
ports: !override
|
||||||
|
- "127.0.0.1:8880:80"
|
||||||
|
|
||||||
|
mbot:
|
||||||
|
build: ../stoat-mbot
|
||||||
|
restart: always
|
||||||
|
env_file: ../stoat-mbot/.env
|
||||||
|
depends_on:
|
||||||
|
api:
|
||||||
|
condition: service_started
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3005:3005"
|
||||||
|
volumes:
|
||||||
|
- ../stoat-mbot/data:/data
|
||||||
Reference in New Issue
Block a user