diff --git a/.gitignore b/.gitignore index bc10988..1e62d61 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,5 @@ SECURITY_AUDIT.md .npm/ .tmp/ tmp/ + +.env.example diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index ed021f3..3df84af 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -120,16 +120,28 @@ app.get('/api/ice-servers', authenticateToken, (_req: AuthRequest, res) => { const iceServers: Array<{ urls: string | string[]; username?: string; credential?: string }> = []; // STUN серверы - if (config.stunUrls.length > 0) { - iceServers.push({ urls: config.stunUrls }); + const stunUrls = [...config.stunUrls]; + // Добавляем ваш VPS и как STUN тоже (раз он работает) + if (config.turnUrl) { + const vpsStun = config.turnUrl.replace('turn:', 'stun:'); + if (!stunUrls.includes(vpsStun)) stunUrls.push(vpsStun); + } + + if (stunUrls.length > 0) { + iceServers.push({ urls: stunUrls }); } // TURN сервер (динамическая или статическая аутентификация) if (config.turnUrl) { const urls = config.turnUrl.split(',').map(s => s.trim()); - if (config.turnSecret) { - // Динамический пароль (HMAC-SHA1) + if (config.turnUsername && config.turnPassword) { + iceServers.push({ + urls: urls, + username: config.turnUsername, + credential: config.turnPassword, + }); + } else if (config.turnSecret) { const ttl = 48 * 3600; const timestamp = Math.floor(Date.now() / 1000) + ttl; const username = `${timestamp}:vortex`; @@ -143,13 +155,6 @@ app.get('/api/ice-servers', authenticateToken, (_req: AuthRequest, res) => { username, credential, }); - } else if (config.turnUsername && config.turnPassword) { - // Статичный логин/пароль - iceServers.push({ - urls: urls, - username: config.turnUsername, - credential: config.turnPassword, - }); } } diff --git a/apps/web/src/components/CallModal.tsx b/apps/web/src/components/CallModal.tsx index 9c71be3..452eff7 100644 --- a/apps/web/src/components/CallModal.tsx +++ b/apps/web/src/components/CallModal.tsx @@ -285,12 +285,18 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi }); } - // Handle audio via stable audio element - if (remoteAudioRef.current) { - if (remoteAudioRef.current.srcObject !== stream) { - remoteAudioRef.current.srcObject = stream; + // Handle remote media via the remoteVideoRef + if (remoteVideoRef.current) { + if (remoteVideoRef.current.srcObject !== stream) { + console.log('[WebRTC] Binding stream to video element'); + remoteVideoRef.current.srcObject = stream; + remoteVideoRef.current.load(); } - remoteAudioRef.current.play().catch(e => console.warn('[WebRTC] Audio play failed:', e)); + // Important: Unmute for remote audio to work + remoteVideoRef.current.muted = false; + remoteVideoRef.current.play().catch(e => { + console.warn('[WebRTC] Play failed, waiting for user interaction:', e); + }); } track.onunmute = checkVideo; @@ -309,6 +315,10 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi endCallSafe(); } }; + + pc.oniceconnectionstatechange = () => { + console.log('[WebRTC] ICE Connection state:', pc.iceConnectionState); + }; }, [endCallSafe]); // REMOVED callType dependency to prevent handler reset during call // Start outgoing call @@ -1429,7 +1439,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi return ( -