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
@@ -67,51 +67,39 @@ PUBLIC_URL=https://music.example.com
|
||||
JWT_SECRET=<случайные 32 байта>
|
||||
```
|
||||
|
||||
`STOAT_API_URL` лучше указывать публичный (`https://домен/api`): бот тогда ведёт себя как обычный
|
||||
клиент и получает от `join_call` тот же LiveKit-URL, что и все. Внутренний `http://api:14702` тоже
|
||||
работает, но убедитесь, что LiveKit-URL из ответа резолвится изнутри контейнера.
|
||||
`STOAT_API_URL` указывается публичный (`https://домен/api`): бот ведёт себя как обычный клиент —
|
||||
ходит в API, gateway и LiveKit через тот же внешний прокси с валидным TLS, что и браузеры.
|
||||
|
||||
### 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
|
||||
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
|
||||
volumes:
|
||||
- ../stoat-mbot/data:/data
|
||||
extra_hosts:
|
||||
- "chat.example.com:host-gateway"
|
||||
```
|
||||
|
||||
Сервис попадает в тот же compose-проект и ту же сеть, поэтому Caddy видит его как `http://mbot:3005`,
|
||||
а бот ходит в Stoat API по имени `api`.
|
||||
Эта запись заставляет контейнер резолвить домен инстанса в сам хост, где внешний Caddy держит
|
||||
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
|
||||
http://music.example.com {
|
||||
reverse_proxy http://mbot:3005
|
||||
music.example.com {
|
||||
reverse_proxy 127.0.0.1:3005
|
||||
}
|
||||
```
|
||||
|
||||
Схема `http://` — потому что у вас TLS терминирует внешний прокси (Caddy слушает `127.0.0.1:8880`).
|
||||
WebSocket проксируется автоматически.
|
||||
Затем `sudo systemctl reload caddy`. WebSocket проксируется автоматически.
|
||||
|
||||
### 6. Запустить
|
||||
|
||||
```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`.
|
||||
@@ -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). Что стоит знать:
|
||||
|
||||
Reference in New Issue
Block a user