mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 01:14:58 +03:00
✨ feat(calls): relay работает сразу после установки, без настройки
Звонок через симметричный NAT или VPN без relay не соединяется — значит relay должен стоять из коробки, а не быть отдельным профилем с выделенным IP и сертификатом. Coturn поднимается вместе со стеком на том же адресе: порт 3478 не спорит с 80 и 443, TURN-over-TLS выключен, потому что он и требовал второго адреса. Адреса relay и STUN считаются от адреса установки и появляются в настройках сами; вписанное владельцем по-прежнему побеждает. Deploy и doctor больше не требуют второго публичного адреса, README обоих языков переписан: вместо «опционально, выделенный IP» — какие порты открыть. Проверено: тесты TURN (включая четыре новых на автонастройку), тесты API звонков и адреса установки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1de6239eb4
commit
d9bc540bc3
10 files changed
+157
-102
No files matched your search
@@ -32,7 +32,7 @@
|
||||
- [Step 2. First-run wizard](#step-2-first-run-wizard)
|
||||
- [Step 3. Configure in the UI](#step-3-configure-in-the-ui)
|
||||
- [Website widget](#website-widget)
|
||||
- [Calls relay (optional)](#calls-relay-optional)
|
||||
- [Calls](#calls)
|
||||
- [External file storage (optional)](#external-file-storage-optional)
|
||||
- [Updating](#updating)
|
||||
- [Features](#features)
|
||||
@@ -58,7 +58,7 @@ The platform installs on your own server with a single command. Customer data st
|
||||
| **Server** | Linux, x86_64 |
|
||||
| **Software** | Docker with the Docker Compose plugin |
|
||||
| **Ports** | 80 and 443 open |
|
||||
| **Calls relay (optional)** | A dedicated public IP, port 3478 and UDP range 49160–49999 |
|
||||
| **Ports for calls** | 3478 (UDP and TCP) and the UDP range 49160–49999 |
|
||||
|
||||
A domain is not needed to start. The installation opens by the server's IP address; the domain is set later in the settings.
|
||||
|
||||
@@ -116,15 +116,16 @@ After creating a web widget, add one tag to your site:
|
||||
|
||||
The chat opens in an isolated window on top of the site.
|
||||
|
||||
### Calls relay (optional)
|
||||
### Calls
|
||||
|
||||
Audio and video calls run directly between browsers. If customers or employees sit behind strict NAT or a corporate firewall, enable the TURN relay:
|
||||
Calls work right after the installation. Between browsers the conversation goes directly; when one side sits behind strict NAT or on a VPN it goes through the relay, which starts together with the stack on the same address. Nothing to configure: the relay addresses appear in **Settings → TURN for calls** on their own, derived from the installation address, and are only changed if you run your own server.
|
||||
|
||||
```bash
|
||||
COMPOSE_PROFILES=calls CHATBALLS_CALL_TURN_REALM=<domain> CHATBALLS_TURN_EXTERNAL_IP=<public IP> CHATBALLS_TURN_LISTENING_IP=<IP for TURN> docker compose up -d --wait
|
||||
```
|
||||
Open on the firewall:
|
||||
|
||||
The relay listens on a dedicated IP so that port 443 does not conflict with the web gateway. The certificate for TURN over TLS is placed in the directory set by `CHATBALLS_TURN_CERTS_DIR`. TURN addresses are then entered in **Settings → Communication**.
|
||||
- 3478/udp and 3478/tcp — the relay itself;
|
||||
- 49160–49999/udp — the conversation ports (two per call).
|
||||
|
||||
Networks that allow nothing but port 443 will not reach the relay on 3478. They need TURN over TLS on 443, which means a separate public address (443 on the main one belongs to the web gateway) or an external TURN service — its addresses go into the same settings.
|
||||
|
||||
### External file storage (optional)
|
||||
|
||||
@@ -270,7 +271,7 @@ docker compose logs worker
|
||||
<details>
|
||||
<summary><strong>Calls do not connect</strong></summary>
|
||||
|
||||
Between browsers a call goes directly. If one side is behind strict NAT, a relay is needed: enable the `calls` profile and enter the TURN addresses in **Settings**. Check that port 3478 and the UDP range 49160–49999 are open on the firewall. Make sure the relay listens on a separate IP and does not overlap with the web gateway on port 443.
|
||||
Between browsers a call goes directly; behind strict NAT and on a VPN it goes through the relay. Check that the `coturn` container runs (`docker compose ps coturn`) and that 3478/udp, 3478/tcp and the 49160–49999/udp range are open on the firewall. The addresses in **Settings → TURN for calls** must not be empty: they are derived from the installation address, so that address has to be set first.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
+10
-9
@@ -32,7 +32,7 @@
|
||||
- [Шаг 2. Мастер первого запуска](#шаг-2-мастер-первого-запуска)
|
||||
- [Шаг 3. Настройка в интерфейсе](#шаг-3-настройка-в-интерфейсе)
|
||||
- [Виджет на сайте](#виджет-на-сайте)
|
||||
- [Звонки через relay (опционально)](#звонки-через-relay-опционально)
|
||||
- [Звонки](#звонки)
|
||||
- [Внешнее хранилище файлов (опционально)](#внешнее-хранилище-файлов-опционально)
|
||||
- [Обновление](#обновление)
|
||||
- [Функции](#функции)
|
||||
@@ -58,7 +58,7 @@ Chatballs берёт на себя первую линию общения с к
|
||||
| **Сервер** | Linux, x86_64 |
|
||||
| **ПО** | Docker и плагин Docker Compose |
|
||||
| **Порты** | 80 и 443 открыты |
|
||||
| **Relay для звонков (опционально)** | Выделенный публичный IP, порт 3478 и диапазон UDP 49160–49999 |
|
||||
| **Порты для звонков** | 3478 (UDP и TCP) и диапазон UDP 49160–49999 |
|
||||
|
||||
Домен на старте не нужен. Установка открывается по IP-адресу сервера, домен задаётся позже в настройках.
|
||||
|
||||
@@ -116,15 +116,16 @@ docker compose up -d --wait
|
||||
|
||||
Чат откроется в изолированном окне поверх сайта.
|
||||
|
||||
### Звонки через relay (опционально)
|
||||
### Звонки
|
||||
|
||||
Аудио- и видеозвонки работают напрямую между браузерами. Если клиенты или сотрудники сидят за строгим NAT или корпоративным файрволом, включите TURN-relay:
|
||||
Звонки работают сразу после установки. Между браузерами разговор идёт напрямую, а если одна из сторон за строгим NAT или в VPN — через relay, который поднимается вместе со стеком на том же адресе. Настраивать нечего: адреса relay появляются в **Настройки → TURN для звонков** сами, от адреса установки, и меняются только если вы ставите свой сервер.
|
||||
|
||||
```bash
|
||||
COMPOSE_PROFILES=calls CHATBALLS_CALL_TURN_REALM=<домен> CHATBALLS_TURN_EXTERNAL_IP=<публичный IP> CHATBALLS_TURN_LISTENING_IP=<IP для TURN> docker compose up -d --wait
|
||||
```
|
||||
На файрволе нужно открыть:
|
||||
|
||||
Relay слушает выделенный IP, чтобы порт 443 не конфликтовал с веб-шлюзом. Сертификат для TURN-over-TLS кладётся в каталог, заданный переменной `CHATBALLS_TURN_CERTS_DIR`. Адреса TURN затем указываются в **Настройки → Коммуникации**.
|
||||
- 3478/udp и 3478/tcp — сам relay;
|
||||
- 49160–49999/udp — порты разговоров (по два на звонок).
|
||||
|
||||
Сети, где наружу разрешён только порт 443, relay на 3478 не пройдут. Для них нужен TURN-over-TLS на 443, а это отдельный публичный адрес (443 на основном занят веб-шлюзом) либо внешний TURN-сервис — его адреса вписываются в те же настройки.
|
||||
|
||||
### Внешнее хранилище файлов (опционально)
|
||||
|
||||
@@ -270,7 +271,7 @@ docker compose logs worker
|
||||
<details>
|
||||
<summary><strong>Звонки не соединяются</strong></summary>
|
||||
|
||||
Между браузерами звонок идёт напрямую. Если одна из сторон за строгим NAT, нужен relay: включите профиль `calls` и укажите адреса TURN в **Настройках**. Проверьте, что на файрволе открыты порт 3478 и диапазон UDP 49160–49999. Убедитесь, что relay слушает отдельный IP и не пересекается с веб-шлюзом по порту 443.
|
||||
Между браузерами звонок идёт напрямую, за строгим NAT и в VPN — через relay. Проверьте, что контейнер `coturn` работает (`docker compose ps coturn`), а на файрволе открыты 3478/udp, 3478/tcp и диапазон 49160–49999/udp. В **Настройки → TURN для звонков** адреса должны быть непустыми: они строятся от адреса установки, поэтому сначала должен быть задан сам адрес.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.conf import settings
|
||||
|
||||
from chatballs.calls.models import CallSession
|
||||
from chatballs.calls.turn import turn_credentials
|
||||
from chatballs.identity.instance_settings import turn_config
|
||||
from chatballs.identity.instance_settings import default_stun_urls, turn_config
|
||||
|
||||
|
||||
def _iso(value):
|
||||
@@ -67,8 +67,9 @@ def ice_servers_payload() -> list[dict]:
|
||||
# fallback с краткоживущими credentials. Генерируется на каждый запрос токена,
|
||||
# поэтому клиент всегда получает не истёкшие TURN credentials.
|
||||
servers: list[dict] = []
|
||||
if settings.CHATBALLS_CALL_STUN_URLS:
|
||||
servers.append({"urls": list(settings.CHATBALLS_CALL_STUN_URLS)})
|
||||
stun_urls = list(settings.CHATBALLS_CALL_STUN_URLS) or default_stun_urls()
|
||||
if stun_urls:
|
||||
servers.append({"urls": stun_urls})
|
||||
turn_urls, ttl = turn_config()
|
||||
if turn_urls and settings.CHATBALLS_CALL_TURN_SECRET:
|
||||
username, credential = turn_credentials(ttl_seconds=ttl)
|
||||
|
||||
@@ -3,10 +3,11 @@ import hashlib
|
||||
import hmac
|
||||
import time
|
||||
|
||||
from django.test import SimpleTestCase, override_settings
|
||||
from django.test import SimpleTestCase, TestCase, override_settings
|
||||
|
||||
from chatballs.calls.serializers import ice_servers_payload
|
||||
from chatballs.calls.turn import turn_credentials
|
||||
from chatballs.identity.instance_settings import InstanceSettings, turn_config
|
||||
|
||||
SECRET = "coturn-shared-secret"
|
||||
TURN_URLS = ["turn:example.com:3478?transport=udp", "turn:example.com:3478?transport=tcp"]
|
||||
@@ -71,3 +72,56 @@ class IceServersPayloadTests(SimpleTestCase):
|
||||
def test_turn_urls_without_secret_are_not_exposed(self) -> None:
|
||||
# Без секрета выдать рабочие credentials нельзя — TURN не отдаётся вовсе.
|
||||
self.assertEqual(ice_servers_payload(), [])
|
||||
|
||||
|
||||
@override_settings(
|
||||
CHATBALLS_CALL_STUN_URLS=[],
|
||||
CHATBALLS_CALL_TURN_URLS=[],
|
||||
CHATBALLS_CALL_TURN_SECRET=SECRET,
|
||||
CHATBALLS_CALL_TURN_TTL_SECONDS=3600,
|
||||
)
|
||||
class TurnDefaultsTests(TestCase):
|
||||
"""Relay коробки: адреса берутся от адреса установки, без единой настройки."""
|
||||
|
||||
def _set_host(self, host: str) -> None:
|
||||
row = InstanceSettings.load()
|
||||
row.public_host = host
|
||||
row.save()
|
||||
|
||||
def test_addresses_come_from_the_installation_address(self) -> None:
|
||||
self._set_host("crm.example.com")
|
||||
urls, _ttl = turn_config()
|
||||
self.assertEqual(
|
||||
urls,
|
||||
[
|
||||
"turn:crm.example.com:3478?transport=udp",
|
||||
"turn:crm.example.com:3478?transport=tcp",
|
||||
],
|
||||
)
|
||||
|
||||
def test_owner_addresses_win_over_the_defaults(self) -> None:
|
||||
row = InstanceSettings.load()
|
||||
row.public_host = "crm.example.com"
|
||||
row.turn_urls = "turns:turn.example.net:5349?transport=tcp"
|
||||
row.save()
|
||||
urls, _ttl = turn_config()
|
||||
self.assertEqual(urls, ["turns:turn.example.net:5349?transport=tcp"])
|
||||
|
||||
def test_ice_payload_offers_stun_and_turn_of_the_box(self) -> None:
|
||||
self._set_host("crm.example.com")
|
||||
servers = ice_servers_payload()
|
||||
self.assertEqual(servers[0], {"urls": ["stun:crm.example.com:3478"]})
|
||||
self.assertEqual(
|
||||
servers[1]["urls"],
|
||||
[
|
||||
"turn:crm.example.com:3478?transport=udp",
|
||||
"turn:crm.example.com:3478?transport=tcp",
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
servers[1]["credential"], _expected_credential(servers[1]["username"])
|
||||
)
|
||||
|
||||
def test_without_the_installation_address_there_is_nothing_to_offer(self) -> None:
|
||||
self._set_host("")
|
||||
self.assertEqual(ice_servers_payload(), [])
|
||||
@@ -258,11 +258,39 @@ def email_from_address() -> str:
|
||||
return str(settings.DEFAULT_FROM_EMAIL)
|
||||
|
||||
|
||||
def turn_config() -> tuple[list[str], int]:
|
||||
"""Адреса TURN и время жизни credentials из настроек установки.
|
||||
# Порт relay в коробке: на нём coturn слушает и TURN, и STUN (compose.yaml).
|
||||
TURN_PORT = 3478
|
||||
|
||||
Пустой список означает «relay не настроен»: звонки идут напрямую и через
|
||||
STUN. Переменная окружения, если задана, побеждает.
|
||||
|
||||
def default_turn_urls(host: str = "") -> list[str]:
|
||||
"""Адреса relay, которые работают в коробке без единой настройки.
|
||||
|
||||
Relay стоит на том же сервере и на том же адресе, что и сама установка, —
|
||||
адрес известен с мастера первого запуска, и заставлять человека вписывать
|
||||
его руками незачем. UDP идёт первым, TCP — запасным для сетей, где UDP
|
||||
режут.
|
||||
"""
|
||||
host = host or public_host()
|
||||
if not host:
|
||||
return []
|
||||
return [
|
||||
f"turn:{host}:{TURN_PORT}?transport=udp",
|
||||
f"turn:{host}:{TURN_PORT}?transport=tcp",
|
||||
]
|
||||
|
||||
|
||||
def default_stun_urls(host: str = "") -> list[str]:
|
||||
"""STUN отдаёт тот же coturn на том же порту."""
|
||||
host = host or public_host()
|
||||
return [f"stun:{host}:{TURN_PORT}"] if host else []
|
||||
|
||||
|
||||
def turn_config() -> tuple[list[str], int]:
|
||||
"""Адреса TURN и время жизни credentials.
|
||||
|
||||
Порядок: переменная окружения (если её всё-таки задали), затем то, что
|
||||
владелец вписал в «Настройки», затем адреса коробки по адресу установки.
|
||||
Пустой список остаётся только там, где адрес установки ещё не известен.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
@@ -276,6 +304,6 @@ def turn_config() -> tuple[list[str], int]:
|
||||
# настройках процесса.
|
||||
return [], settings.CHATBALLS_CALL_TURN_TTL_SECONDS
|
||||
if row is None or not row.turn_urls.strip():
|
||||
return [], settings.CHATBALLS_CALL_TURN_TTL_SECONDS
|
||||
return default_turn_urls(), settings.CHATBALLS_CALL_TURN_TTL_SECONDS
|
||||
urls = [line.strip() for line in row.turn_urls.splitlines() if line.strip()]
|
||||
return urls, row.turn_ttl_seconds or settings.CHATBALLS_CALL_TURN_TTL_SECONDS
|
||||
@@ -17,6 +17,7 @@ from chatballs.i18n.languages import DEFAULT_LANGUAGE, LANGUAGES, normalize_lang
|
||||
from chatballs.identity.instance_access import InstanceSettingsPermission, IsInstanceAdmin
|
||||
from chatballs.identity.instance_settings import (
|
||||
InstanceSettings,
|
||||
default_turn_urls,
|
||||
email_connection,
|
||||
email_from_address,
|
||||
invalidate_cache,
|
||||
@@ -50,7 +51,13 @@ def instance_payload(row: InstanceSettings) -> dict:
|
||||
"turn": {
|
||||
# Секрет общий с coturn и лежит в томе секретов: наружу не отдаём
|
||||
# и в настройках не показываем — вводить его человеку не нужно.
|
||||
"urls": [line for line in row.turn_urls.splitlines() if line.strip()],
|
||||
#
|
||||
# Адреса показываются действующие: пока владелец их не менял, это
|
||||
# relay коробки на адресе установки. Пустое поле означало бы, что
|
||||
# звонки через relay не работают, пока человек что-то впишет, —
|
||||
# а они работают сразу после установки.
|
||||
"urls": [line for line in row.turn_urls.splitlines() if line.strip()]
|
||||
or default_turn_urls(),
|
||||
"ttlSeconds": row.turn_ttl_seconds,
|
||||
"secretReady": bool(settings.CHATBALLS_CALL_TURN_SECRET),
|
||||
},
|
||||
|
||||
+28
-29
@@ -279,13 +279,21 @@ services:
|
||||
backend-platform:
|
||||
condition: service_healthy
|
||||
|
||||
# Coturn — медиа-relay для TURN fallback (SPEC-CHATBALLS-0013 §11).
|
||||
# Опциональный profile `calls` (ADR-CHATBALLS-0028): включается через COMPOSE_PROFILES=calls.
|
||||
# Host networking: relay использует широкий UDP-диапазон и реальный внешний IP,
|
||||
# публикуется напрямую и не проходит через HTTP reverse proxy. credentials
|
||||
# выдаёт backend по общему CHATBALLS_CALL_TURN_SECRET (static-auth-secret).
|
||||
# Coturn — медиа-relay для звонков (SPEC-CHATBALLS-0013 §11).
|
||||
#
|
||||
# Поднимается вместе со всей установкой и слушает тот же адрес, что и веб:
|
||||
# звонок через симметричный NAT или VPN без relay не соединяется в принципе,
|
||||
# и превращать это в отдельную настройку значит выдавать клиенту заведомо
|
||||
# неработающие звонки. Порт 3478 не спорит с 80 и 443, так что второй IP не
|
||||
# нужен; TURN-over-TLS по умолчанию выключен — он требует сертификата и
|
||||
# 443-го порта, то есть как раз выделенного адреса. Кому нужен TLS (сети,
|
||||
# где наружу разрешён только 443), включает его отдельным compose-override.
|
||||
#
|
||||
# Host networking: relay использует широкий UDP-диапазон и реальный внешний
|
||||
# IP, публикуется напрямую и не проходит через HTTP reverse proxy.
|
||||
# credentials выдаёт backend по общему CHATBALLS_CALL_TURN_SECRET
|
||||
# (static-auth-secret); адреса relay backend строит от адреса установки.
|
||||
coturn:
|
||||
profiles: ["calls"]
|
||||
image: ${CHATBALLS_COTURN_IMAGE:-coturn/coturn:4.6}
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
@@ -299,40 +307,31 @@ services:
|
||||
# старт, человек его не вводит и не дублирует в двух местах.
|
||||
- -c
|
||||
- /run/chatballs/secrets/turnserver-secret.conf
|
||||
- --realm=${CHATBALLS_CALL_TURN_REALM:-}
|
||||
# Весь TURN живёт на выделенном публичном IP (отдельный порт/NIC), Caddy — на
|
||||
# основном IP. Это освобождает 443 под TURN-over-TLS без конфликта с web.
|
||||
- --listening-ip=${CHATBALLS_TURN_LISTENING_IP:-0.0.0.0}
|
||||
- --relay-ip=${CHATBALLS_TURN_EXTERNAL_IP:-}
|
||||
- --external-ip=${CHATBALLS_TURN_EXTERNAL_IP:-}
|
||||
- --listening-port=${CHATBALLS_TURN_LISTENING_PORT:-3478}
|
||||
# TURN-over-TLS на 443: проходит через VPN/строгие сети, где UDP и 3478 режут.
|
||||
- --tls-listening-port=${CHATBALLS_TURN_TLS_PORT:-443}
|
||||
- --cert=/etc/coturn/certs/fullchain.pem
|
||||
- --pkey=/etc/coturn/certs/privkey.pem
|
||||
- --min-port=${CHATBALLS_TURN_MIN_PORT:-49160}
|
||||
# realm участвует только в digest-аутентификации; клиент узнаёт его от
|
||||
# самого сервера, поэтому значение фиксированное.
|
||||
- --realm=chatballs
|
||||
- --listening-port=3478
|
||||
# TLS и DTLS выключены: без сертификата они всё равно не поднялись бы, а
|
||||
# сертификат означает 443 и выделенный адрес.
|
||||
- --no-tls
|
||||
- --no-dtls
|
||||
- --min-port=49160
|
||||
# Relay-диапазон UDP (1 порт на media-endpoint; ~2 порта на звонок).
|
||||
# 49160-49999 = 840 портов ≈ 420 одновременных TURN-звонков на хосте.
|
||||
# Расширяется через CHATBALLS_TURN_MIN_PORT/CHATBALLS_TURN_MAX_PORT; все порты диапазона
|
||||
# должны быть открыты на firewall и не пересекаться с ephemeral-диапазоном ОС.
|
||||
- --max-port=${CHATBALLS_TURN_MAX_PORT:-49999}
|
||||
# Все порты диапазона должны быть открыты на firewall и не пересекаться
|
||||
# с ephemeral-диапазоном ОС.
|
||||
- --max-port=49999
|
||||
# Запрет анонимного и внутрисетевого relay (SPEC §11: без пересечения с хостом).
|
||||
- --no-multicast-peers
|
||||
- --no-tcp-relay
|
||||
- --denied-peer-ip=10.0.0.0-10.255.255.255
|
||||
- --denied-peer-ip=172.16.0.0-172.31.255.255
|
||||
- --denied-peer-ip=192.168.0.0-192.168.255.255
|
||||
- --no-tlsv1
|
||||
- --no-tlsv1_1
|
||||
volumes:
|
||||
# LE-сертификат TURN-хоста (turns:) кладёт на хост renewal deploy-hook —
|
||||
# это единственное место, где стек смотрит наружу файлом, и только при
|
||||
# включённом профиле calls. Каталог задаётся явно.
|
||||
- chatballs-secrets:/run/chatballs/secrets:ro
|
||||
- ${CHATBALLS_TURN_CERTS_DIR:-./data/coturn-certs}:/etc/coturn/certs:ro
|
||||
healthcheck:
|
||||
# Allocation smoke: STUN binding к собственному listener на выделенном IP.
|
||||
test: ["CMD", "turnutils_stunclient", "-p", "${CHATBALLS_TURN_LISTENING_PORT:-3478}", "${CHATBALLS_TURN_LISTENING_IP:-0.0.0.0}"]
|
||||
# Allocation smoke: STUN binding к собственному listener.
|
||||
test: ["CMD", "turnutils_stunclient", "-p", "3478", "127.0.0.1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -97,27 +97,6 @@ verify_release_checksums() {
|
||||
}
|
||||
}
|
||||
|
||||
validate_calls_network_boundary() {
|
||||
# Профиль calls включают переменной окружения — тем же способом, каким её
|
||||
# читает сам compose. Файла с конфигурацией у установки нет.
|
||||
local web_ip turn_ip
|
||||
web_ip="${CHATBALLS_WEB_LISTENING_IP:-}"
|
||||
turn_ip="${CHATBALLS_TURN_LISTENING_IP:-}"
|
||||
|
||||
[[ -n "$web_ip" ]] || {
|
||||
log_err "CHATBALLS_WEB_LISTENING_IP is required for calls profile"
|
||||
return 1
|
||||
}
|
||||
[[ "$web_ip" != "0.0.0.0" ]] || {
|
||||
log_err "CHATBALLS_WEB_LISTENING_IP cannot be 0.0.0.0 when calls profile uses TURN TLS on 443"
|
||||
return 1
|
||||
}
|
||||
[[ "$web_ip" != "$turn_ip" ]] || {
|
||||
log_err "web and TURN listeners must use different public IP addresses"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
release_image_keys() {
|
||||
printf '%s\n' \
|
||||
CHATBALLS_BACKEND_IMAGE \
|
||||
|
||||
@@ -24,10 +24,7 @@ cmd_deploy() {
|
||||
run_compose run --rm init || die "deploy: init (migrate) failed" 1
|
||||
|
||||
log "deploy: starting application services"
|
||||
local app_services=(backend-app backend-platform backend-admin worker frontend gateway)
|
||||
if profile_enabled calls; then
|
||||
app_services+=(coturn)
|
||||
fi
|
||||
local app_services=(backend-app backend-platform backend-admin worker frontend gateway coturn)
|
||||
run_compose up -d "${app_services[@]}" || die "deploy: application start failed" 1
|
||||
|
||||
log "deploy: waiting for application health"
|
||||
@@ -36,9 +33,8 @@ cmd_deploy() {
|
||||
_wait_running backend-admin 30 || die "deploy: admin backend did not start" 1
|
||||
_wait_running frontend 30 || die "deploy: frontend did not start" 1
|
||||
_wait_running gateway 30 || die "deploy: gateway did not start" 1
|
||||
if profile_enabled calls; then
|
||||
_wait_healthy coturn 60 || die "deploy: coturn did not become healthy" 1
|
||||
fi
|
||||
# Relay поднимается вместе со всеми: звонки за NAT без него не соединяются.
|
||||
_wait_healthy coturn 60 || die "deploy: coturn did not become healthy" 1
|
||||
|
||||
log "deploy: running smoke checks"
|
||||
_smoke || die "deploy: smoke checks failed" 1
|
||||
@@ -57,10 +53,6 @@ _deploy_validate() {
|
||||
# Домены здесь не проверяются: установка отвечает по адресу сервера, а свой
|
||||
# домен владелец задаёт в «Настройках» — снаружи его знать неоткуда.
|
||||
|
||||
if profile_enabled calls; then
|
||||
validate_calls_network_boundary || return 1
|
||||
fi
|
||||
|
||||
compose_config_validate >/dev/null 2>&1 || {
|
||||
log_err "compose config invalid"
|
||||
return 1
|
||||
|
||||
@@ -85,19 +85,12 @@ cmd_doctor() {
|
||||
# прежние проверки просто ругались на исправную установку — они читали
|
||||
# instance .env, которого у продукта нет.
|
||||
|
||||
if profile_enabled calls; then
|
||||
local missing=0 k
|
||||
for k in CHATBALLS_CALL_TURN_REALM CHATBALLS_TURN_EXTERNAL_IP CHATBALLS_TURN_LISTENING_IP; do
|
||||
if [[ -z "${!k:-}" ]]; then
|
||||
_doctor_report 0 "$k required for calls profile"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [[ "$missing" == "0" ]] && validate_calls_network_boundary; then
|
||||
_doctor_report 1 "calls profile network boundary valid"
|
||||
else
|
||||
_doctor_report 0 "calls profile network boundary invalid"
|
||||
fi
|
||||
# Relay стоит на том же адресе, что и веб: порт 3478 не спорит с 80 и 443,
|
||||
# поэтому проверять нечего, кроме того, что он поднялся.
|
||||
if run_compose ps --status running --services 2>/dev/null | grep -qx coturn; then
|
||||
_doctor_report 1 "calls relay (coturn) is running"
|
||||
else
|
||||
_doctor_report 0 "calls relay (coturn) is not running — calls behind NAT will fail"
|
||||
fi
|
||||
|
||||
if { [[ -d "$inst/backups" ]] && [[ -w "$inst/backups" ]]; } || [[ -w "$inst" ]]; then
|
||||
|
||||
Reference in new issue
Block a user