mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
fix: resolve LiveKit connection issues
- Add http://localhost:* and ws://localhost:* to CSP connect-src (WebView2 was blocking LiveKit signal connection) - Add connection retry (3 attempts, 2s delay) for LiveKit server startup race condition ("could not find any available nodes") - Remove broken TURN TLS config from generated livekit.yaml - Send direct LiveKit URL instead of proxy path (localhost is treated as secure context in Chromium/WebView2) - Set LiveKit server host from API config for URL resolution
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: wss:; img-src 'self' https: data:; frame-src https://www.youtube.com https://youtube.com"
|
||||
"csp": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: wss: http://localhost:* ws://localhost:* http://127.0.0.1:* ws://127.0.0.1:*; img-src 'self' https: data:; frame-src https://www.youtube.com https://youtube.com"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
||||
@@ -320,9 +320,38 @@ export async function handleVoiceToken(
|
||||
room.on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakersChanged);
|
||||
room.on(RoomEvent.Disconnected, handleDisconnected);
|
||||
|
||||
// Connect to LiveKit server (resolve proxy URL if relative path)
|
||||
// Connect to LiveKit server with retry (LiveKit may still be initializing)
|
||||
const resolvedUrl = resolveLiveKitUrl(url);
|
||||
await room.connect(resolvedUrl, token);
|
||||
const MAX_RETRIES = 3;
|
||||
const RETRY_DELAY_MS = 2000;
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
await room.connect(resolvedUrl, token);
|
||||
break;
|
||||
} catch (connectErr) {
|
||||
if (attempt < MAX_RETRIES) {
|
||||
log.warn("LiveKit connect failed, retrying", { attempt, maxRetries: MAX_RETRIES, error: connectErr });
|
||||
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
||||
// Recreate room for fresh connection state
|
||||
room.removeAllListeners();
|
||||
room = new Room({
|
||||
adaptiveStream: true,
|
||||
dynacast: true,
|
||||
audioCaptureDefaults: {
|
||||
echoCancellation: loadPref("echoCancellation", true),
|
||||
noiseSuppression: loadPref("noiseSuppression", true),
|
||||
autoGainControl: loadPref("autoGainControl", true),
|
||||
},
|
||||
});
|
||||
room.on(RoomEvent.TrackSubscribed, handleTrackSubscribed);
|
||||
room.on(RoomEvent.TrackUnsubscribed, handleTrackUnsubscribed);
|
||||
room.on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakersChanged);
|
||||
room.on(RoomEvent.Disconnected, handleDisconnected);
|
||||
} else {
|
||||
throw connectErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("Connected to LiveKit room", { channelId, url: resolvedUrl });
|
||||
|
||||
// Enable microphone: use RNNoise if Enhanced Noise Suppression is on
|
||||
|
||||
@@ -46,22 +46,8 @@ func NewLiveKitProcess(cfg *config.VoiceConfig, tlsCfg *config.TLSConfig, dataDi
|
||||
func (p *LiveKitProcess) generateConfig() (string, error) {
|
||||
cfgPath := filepath.Join(p.dataDir, "livekit.yaml")
|
||||
|
||||
// Build TLS section if OwnCord has TLS configured with cert files.
|
||||
tlsSection := ""
|
||||
if p.tlsCfg != nil && p.tlsCfg.CertFile != "" && p.tlsCfg.KeyFile != "" {
|
||||
// Resolve cert/key paths relative to the data directory's parent
|
||||
// (same working directory as chatserver).
|
||||
certFile := p.tlsCfg.CertFile
|
||||
keyFile := p.tlsCfg.KeyFile
|
||||
tlsSection = fmt.Sprintf(`
|
||||
turn:
|
||||
enabled: true
|
||||
tls_port: 5349
|
||||
udp_port: 3478
|
||||
cert_file: %s
|
||||
key_file: %s
|
||||
`, certFile, keyFile)
|
||||
}
|
||||
// No TURN TLS config — LiveKit signaling is proxied through OwnCord's
|
||||
// HTTPS server at /livekit/*, so no separate TLS is needed on LiveKit.
|
||||
|
||||
content := fmt.Sprintf(`# Auto-generated by OwnCord — do not edit manually.
|
||||
port: 7880
|
||||
@@ -70,10 +56,10 @@ rtc:
|
||||
port_range_end: 60000
|
||||
use_external_ip: true
|
||||
keys:
|
||||
%s: %s%s
|
||||
%s: %s
|
||||
logging:
|
||||
level: info
|
||||
`, p.cfg.LiveKitAPIKey, p.cfg.LiveKitAPISecret, tlsSection)
|
||||
`, p.cfg.LiveKitAPIKey, p.cfg.LiveKitAPISecret)
|
||||
|
||||
if err := os.MkdirAll(p.dataDir, 0o755); err != nil {
|
||||
return "", fmt.Errorf("creating data dir: %w", err)
|
||||
|
||||
@@ -103,10 +103,7 @@ func (h *Hub) handleVoiceJoin(c *Client, payload json.RawMessage) {
|
||||
slog.Error("ws handleVoiceJoin GenerateToken", "err", tokenErr, "user_id", c.userID)
|
||||
// Non-fatal: voice join still succeeds at the DB/state level.
|
||||
} else {
|
||||
// Send "/livekit" as URL — client constructs the full wss:// URL
|
||||
// from its server connection. Proxied through OwnCord's HTTPS
|
||||
// to avoid mixed-content blocks in WebView2.
|
||||
c.sendMsg(buildVoiceToken(channelID, token, "/livekit"))
|
||||
c.sendMsg(buildVoiceToken(channelID, token, h.livekit.URL()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user