Проверка
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -72,3 +72,5 @@ SECURITY_AUDIT.md
|
||||
.npm/
|
||||
.tmp/
|
||||
tmp/
|
||||
|
||||
.env.example
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<AnimatePresence>
|
||||
<audio key="remote-audio" ref={remoteAudioRef} autoPlay playsInline />
|
||||
{/* Remote audio is handled by the video element below */}
|
||||
|
||||
{/* === MINIMIZED VIEW === */}
|
||||
{isMinimized && callState === 'connected' ? (
|
||||
@@ -1577,7 +1587,6 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
||||
ref={remoteVideoRef}
|
||||
autoPlay
|
||||
playsInline
|
||||
muted
|
||||
className={`
|
||||
bg-black transition-all duration-500
|
||||
${showVideoArea
|
||||
|
||||
Reference in New Issue
Block a user