fix(voice): отдавать клиенту базовый адрес LiveKit без сегмента /rtc

LiveKit SDK сам дописывает сигнальный путь (`/rtc`, `/rtc/v1`) к базовому
адресу, поэтому `wss://host/rtc` из конфига превращался в `wss://host/rtc/rtc`
и комната навсегда оставалась в состоянии «Подключение…» (AGENT.md 7.14).

- `voice.ResolveLiveKitURL` снимает завершающий сегмент `/rtc`, сохраняя
  префикс прокси (`wss://host/livekit/rtc` → `wss://host/livekit`);
- `POST /channels/{id}/voice/join` отдаёт адрес через `VoiceURLForClient` и
  отвечает `voice.disabled`, если адрес не разрешился;
- тесты: таблица адресов, ожидание `wss://gl.test` в голосовом сценарии.
This commit is contained in:
2026-09-20 19:29:19 +03:00
parent 1552146aa1
commit 2538e07ec3
4 changed files with 49 additions and 8 deletions
+19 -3
View File
@@ -9,6 +9,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/url"
"strings"
"time"
)
@@ -241,8 +242,11 @@ func subtleCompare(a, b string) bool {
return hmac.Equal([]byte(a), []byte(b))
}
// ResolveLiveKitURL приводит адрес к виду, понятному браузеру: /rtc проксируется
// тем же доменом, поэтому оставляем как есть, но проверяем схему.
// ResolveLiveKitURL приводит публичный адрес к базовому виду, который ждёт
// LiveKit SDK: клиент сам дописывает сигнальный путь (`/rtc`, `/rtc/v1`),
// поэтому сегмент `/rtc` из адреса убираем — иначе брандмауэр клиента стучится
// в `/rtc/rtc` и комната остаётся в состоянии «Подключение…». Префикс прокси
// сохраняется: `wss://host/livekit/rtc` → `wss://host/livekit`.
func ResolveLiveKitURL(raw string) (string, error) {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
@@ -251,7 +255,19 @@ func ResolveLiveKitURL(raw string) (string, error) {
if !strings.HasPrefix(trimmed, "ws://") && !strings.HasPrefix(trimmed, "wss://") {
return "", fmt.Errorf("voice.invalid_url: %s", trimmed)
}
return strings.TrimSuffix(trimmed, "/"), nil
parsed, err := url.Parse(trimmed)
if err != nil {
return "", fmt.Errorf("voice.invalid_url: %s", trimmed)
}
path := strings.TrimSuffix(parsed.Path, "/")
if path != "/rtc" {
path = strings.TrimSuffix(path, "/rtc")
} else {
path = ""
}
parsed.Path = path
parsed.RawPath = ""
return strings.TrimSuffix(parsed.String(), "/"), nil
}
// VoiceURLForClient отдаёт адрес LiveKit так, как его должен использовать