Проверка
This commit is contained in:
@@ -72,3 +72,5 @@ SECURITY_AUDIT.md
|
|||||||
.npm/
|
.npm/
|
||||||
.tmp/
|
.tmp/
|
||||||
tmp/
|
tmp/
|
||||||
|
|
||||||
|
.env.example
|
||||||
|
|||||||
+16
-11
@@ -120,16 +120,28 @@ app.get('/api/ice-servers', authenticateToken, (_req: AuthRequest, res) => {
|
|||||||
const iceServers: Array<{ urls: string | string[]; username?: string; credential?: string }> = [];
|
const iceServers: Array<{ urls: string | string[]; username?: string; credential?: string }> = [];
|
||||||
|
|
||||||
// STUN серверы
|
// STUN серверы
|
||||||
if (config.stunUrls.length > 0) {
|
const stunUrls = [...config.stunUrls];
|
||||||
iceServers.push({ urls: 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 сервер (динамическая или статическая аутентификация)
|
// TURN сервер (динамическая или статическая аутентификация)
|
||||||
if (config.turnUrl) {
|
if (config.turnUrl) {
|
||||||
const urls = config.turnUrl.split(',').map(s => s.trim());
|
const urls = config.turnUrl.split(',').map(s => s.trim());
|
||||||
|
|
||||||
if (config.turnSecret) {
|
if (config.turnUsername && config.turnPassword) {
|
||||||
// Динамический пароль (HMAC-SHA1)
|
iceServers.push({
|
||||||
|
urls: urls,
|
||||||
|
username: config.turnUsername,
|
||||||
|
credential: config.turnPassword,
|
||||||
|
});
|
||||||
|
} else if (config.turnSecret) {
|
||||||
const ttl = 48 * 3600;
|
const ttl = 48 * 3600;
|
||||||
const timestamp = Math.floor(Date.now() / 1000) + ttl;
|
const timestamp = Math.floor(Date.now() / 1000) + ttl;
|
||||||
const username = `${timestamp}:vortex`;
|
const username = `${timestamp}:vortex`;
|
||||||
@@ -143,13 +155,6 @@ app.get('/api/ice-servers', authenticateToken, (_req: AuthRequest, res) => {
|
|||||||
username,
|
username,
|
||||||
credential,
|
credential,
|
||||||
});
|
});
|
||||||
} else if (config.turnUsername && config.turnPassword) {
|
|
||||||
// Статичный логин/пароль
|
|
||||||
iceServers.push({
|
|
||||||
urls: urls,
|
|
||||||
username: config.turnUsername,
|
|
||||||
credential: config.turnPassword,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -285,12 +285,18 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle audio via stable audio element
|
// Handle remote media via the remoteVideoRef
|
||||||
if (remoteAudioRef.current) {
|
if (remoteVideoRef.current) {
|
||||||
if (remoteAudioRef.current.srcObject !== stream) {
|
if (remoteVideoRef.current.srcObject !== stream) {
|
||||||
remoteAudioRef.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;
|
track.onunmute = checkVideo;
|
||||||
@@ -309,6 +315,10 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
endCallSafe();
|
endCallSafe();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pc.oniceconnectionstatechange = () => {
|
||||||
|
console.log('[WebRTC] ICE Connection state:', pc.iceConnectionState);
|
||||||
|
};
|
||||||
}, [endCallSafe]); // REMOVED callType dependency to prevent handler reset during call
|
}, [endCallSafe]); // REMOVED callType dependency to prevent handler reset during call
|
||||||
|
|
||||||
// Start outgoing call
|
// Start outgoing call
|
||||||
@@ -1429,7 +1439,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<audio key="remote-audio" ref={remoteAudioRef} autoPlay playsInline />
|
{/* Remote audio is handled by the video element below */}
|
||||||
|
|
||||||
{/* === MINIMIZED VIEW === */}
|
{/* === MINIMIZED VIEW === */}
|
||||||
{isMinimized && callState === 'connected' ? (
|
{isMinimized && callState === 'connected' ? (
|
||||||
@@ -1577,7 +1587,6 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
ref={remoteVideoRef}
|
ref={remoteVideoRef}
|
||||||
autoPlay
|
autoPlay
|
||||||
playsInline
|
playsInline
|
||||||
muted
|
|
||||||
className={`
|
className={`
|
||||||
bg-black transition-all duration-500
|
bg-black transition-all duration-500
|
||||||
${showVideoArea
|
${showVideoArea
|
||||||
|
|||||||
Reference in New Issue
Block a user