connection fixes

This commit is contained in:
2025-10-06 09:41:23 +09:00
parent 4ceccae6ce
commit fa55367e68
361 changed files with 24633 additions and 6206 deletions

View File

@@ -61,40 +61,6 @@ function initializeSocket() {
socket.on('device:disconnected', (data) => {
logMessage('warn', `Устройство отключено: ${data.deviceId}`);
});
// Обработчики событий сессий
socket.on('session:created', (data) => {
logMessage('info', `Сессия создана: ${data.sessionId} для устройства ${data.deviceId}`);
updateOperatorSessions();
});
socket.on('session:accepted', (data) => {
logMessage('info', `Сессия принята: ${data.sessionId}`);
currentSessionId = data.sessionId;
document.getElementById('current-session-id').value = currentSessionId;
// Показываем кнопку веб-камеры для оператора
showOperatorWebcamButton();
updateOperatorSessions();
showAlert('Сессия принята! Можно начинать WebRTC соединение.', 'success');
});
socket.on('session:rejected', (data) => {
logMessage('warn', `Сессия отклонена: ${data.sessionId} - ${data.error}`);
updateOperatorSessions();
showAlert(`Сессия отклонена: ${data.error}`, 'warning');
});
socket.on('session:ended', (data) => {
logMessage('info', `Сессия завершена: ${data.sessionId}`);
updateOperatorSessions();
// Скрываем элементы веб-камеры
hideOperatorWebcamButton();
const webcamVideo = document.getElementById('operatorWebcam');
if (webcamVideo) webcamVideo.style.display = 'none';
});
}
// Обновление статуса подключения
@@ -181,6 +147,8 @@ function connectAndroid() {
});
androidSocket.on('camera:request', (data) => {
console.log('🎥 ПОЛУЧЕН ЗАПРОС КАМЕРЫ:', data);
logMessage('info', `🎥 Получен запрос камеры: ${JSON.stringify(data)}`);
showCameraRequest(data);
});
@@ -213,6 +181,7 @@ function disconnectAndroid() {
}
function showCameraRequest(data) {
console.log('📱 ПОКАЗЫВАЕМ ЗАПРОС КАМЕРЫ:', data);
const container = document.getElementById('android-requests');
const requestDiv = document.createElement('div');
requestDiv.className = 'session-card session-pending';
@@ -227,19 +196,30 @@ function showCameraRequest(data) {
container.appendChild(requestDiv);
logMessage('info', `Получен запрос камеры от ${data.operatorId}`);
// Автоматически принимаем запрос через 1 секунду для удобства тестирования
console.log('⏰ ЗАПЛАНИРОВАНО АВТОПРИНЯТИЕ ЧЕРЕЗ 1 СЕКУНДУ');
setTimeout(() => {
console.log('⏰ ВЫПОЛНЯЕМ АВТОПРИНЯТИЕ');
acceptCameraRequest(data.sessionId);
}, 1000);
}
function acceptCameraRequest(sessionId) {
console.log('✅ ПРИНЯТИЕ ЗАПРОСА КАМЕРЫ:', sessionId);
if (androidSocket) {
androidSocket.emit('camera:response', {
sessionId,
accepted: true,
streamUrl: 'webrtc'
});
console.log('✅ ОТПРАВЛЕНО camera:response с accepted=true');
// Перемещаем в активные сессии
moveToActiveSessions(sessionId, 'active');
logMessage('info', `Запрос камеры принят: ${sessionId}`);
} else {
console.error('❌ androidSocket НЕ ПОДКЛЮЧЕН!');
}
}
@@ -334,14 +314,6 @@ function connectOperator() {
document.getElementById('operator-disconnect').disabled = false;
showAvailableDevices(data.availableDevices || []);
updateOperatorSessions(); // Обновляем список сессий после подключения
// Автоматическое обновление сессий каждые 3 секунды
setInterval(() => {
if (operatorSocket && operatorSocket.connected) {
updateOperatorSessions();
}
}, 3000);
});
operatorSocket.on('device:connected', (data) => {
@@ -352,12 +324,6 @@ function connectOperator() {
operatorSocket.on('camera:stream-ready', (data) => {
logMessage('info', `Камера готова: ${data.sessionId}`);
showOperatorSession(data.sessionId, 'active');
updateOperatorSessions();
});
operatorSocket.on('camera:request-sent', (data) => {
logMessage('info', `Запрос отправлен: ${data.sessionId}`);
updateOperatorSessions();
});
operatorSocket.on('camera:denied', (data) => {
@@ -545,7 +511,26 @@ async function endOperatorSession(sessionId) {
}
async function refreshSessions() {
updateOperatorSessions();
const operatorId = document.getElementById('operator-id').value;
if (!operatorId) return;
try {
const response = await fetch('/api/operators/sessions', {
headers: { 'X-Operator-Id': operatorId }
});
if (response.ok) {
const data = await response.json();
const container = document.getElementById('operator-sessions');
container.innerHTML = '';
data.sessions.forEach(session => {
showOperatorSession(session.sessionId, session.status);
});
}
} catch (error) {
logMessage('error', 'Ошибка загрузки сессий: ' + error.message);
}
}
// === ADMIN FUNCTIONS ===
@@ -679,9 +664,11 @@ function setupAndroidWebRTC() {
androidSocket.on('webrtc:ice-candidate', async (data) => {
try {
logMessage('info', `📡 Получен ICE candidate: ${JSON.stringify(data.candidate)}`);
const pc = getOrCreatePeerConnection();
const candidate = JSON.parse(data.candidate);
const candidate = data.candidate;
await pc.addIceCandidate(new RTCIceCandidate(candidate));
logMessage('success', '✅ ICE candidate добавлен успешно');
} catch (error) {
logMessage('error', 'ICE candidate ошибка: ' + error.message);
}
@@ -706,9 +693,11 @@ function setupOperatorWebRTC() {
operatorSocket.on('webrtc:ice-candidate', async (data) => {
try {
logMessage('info', `📡 Получен ICE candidate: ${JSON.stringify(data.candidate)}`);
const pc = getOrCreatePeerConnection();
const candidate = JSON.parse(data.candidate);
const candidate = data.candidate;
await pc.addIceCandidate(new RTCIceCandidate(candidate));
logMessage('success', '✅ ICE candidate добавлен успешно');
} catch (error) {
logMessage('error', 'ICE candidate ошибка: ' + error.message);
}
@@ -721,11 +710,13 @@ function getOrCreatePeerConnection() {
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
const candidateData = JSON.stringify({
const candidateData = {
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex
});
};
logMessage('info', `📤 Отправляем ICE candidate: ${JSON.stringify(candidateData)}`);
if (androidSocket) {
androidSocket.emit('webrtc:ice-candidate', {
@@ -809,95 +800,9 @@ function stopLocalVideo() {
}
}
// Обновление списка сессий оператора
function updateOperatorSessions() {
console.log('updateOperatorSessions called, operatorSocket:', operatorSocket);
if (!operatorSocket) {
console.log('No operatorSocket, returning');
return;
}
if (!operatorSocket.connected) {
console.log('operatorSocket not connected, returning');
return;
}
console.log('Requesting sessions list...');
operatorSocket.emit('sessions:list', {}, (sessions) => {
console.log('Received sessions:', sessions);
const container = document.getElementById('operator-sessions');
if (!container) {
console.log('No operator-sessions container found');
return;
}
container.innerHTML = '';
if (!sessions || sessions.length === 0) {
container.innerHTML = '<p>Нет активных сессий</p>';
console.log('No sessions to display');
return;
}
console.log('Displaying', sessions.length, 'sessions');
sessions.forEach(session => {
const sessionCard = document.createElement('div');
sessionCard.className = `session-card session-${session.status}`;
const statusText = {
'pending': 'Ожидание',
'active': 'Активна',
'rejected': 'Отклонена',
'ended': 'Завершена'
};
sessionCard.innerHTML = `
<h4>Сессия: ${session.sessionId ? session.sessionId.substr(0, 8) : session.id ? session.id.substr(0, 8) : 'N/A'}...</h4>
<p><strong>Устройство:</strong> ${session.deviceId}</p>
<p><strong>Камера:</strong> ${session.cameraType}</p>
<p><strong>Статус:</strong> ${statusText[session.status] || session.status}</p>
<p><strong>Создана:</strong> ${new Date(session.createdAt).toLocaleString()}</p>
${session.status === 'active' ? `
<button class="btn" onclick="switchCamera('${session.sessionId || session.id}', 'front')">Фронтальная</button>
<button class="btn" onclick="switchCamera('${session.sessionId || session.id}', 'back')">Основная</button>
<button class="btn btn-danger" onclick="endSession('${session.sessionId || session.id}')">Завершить</button>
` : ''}
`;
container.appendChild(sessionCard);
});
});
}
// Завершение сессии
function endSession(sessionId) {
if (!operatorSocket) {
showAlert('Не подключен как оператор', 'danger');
return;
}
operatorSocket.emit('session:end', { sessionId });
logMessage('info', `Завершаем сессию: ${sessionId}`);
}
// Переключение камеры
function switchCamera(sessionId, cameraType) {
if (!operatorSocket) {
showAlert('Не подключен как оператор', 'danger');
return;
}
operatorSocket.emit('camera:switch', {
sessionId,
cameraType
});
logMessage('info', `Переключаем камеру: ${sessionId} на ${cameraType}`);
}
// Показ уведомления
function showAlert(message, type) {
// Вспомогательная функция для показа уведомлений
function showAlert(type, message) {
// Создаем временное уведомление
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
@@ -909,66 +814,10 @@ function showAlert(message, type) {
document.body.appendChild(alert);
// Удаляем через 5 секунд
setTimeout(() => {
if (document.body.contains(alert)) {
document.body.removeChild(alert);
}
}, 5000);
}
// === ОПЕРАТОР: Веб-камера и WebRTC ===
function showOperatorWebcamButton() {
const btn = document.getElementById('startOperatorWebcam');
if (btn) btn.style.display = '';
}
function hideOperatorWebcamButton() {
const btn = document.getElementById('startOperatorWebcam');
if (btn) btn.style.display = 'none';
}
function startOperatorWebcam() {
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(stream => {
operatorWebcamStream = stream;
const video = document.getElementById('operatorWebcam');
if (video) {
video.srcObject = stream;
video.style.display = '';
}
startOperatorWebRTC(stream);
})
.catch(err => {
showAlert('danger', 'Ошибка доступа к веб-камере: ' + err);
});
}
function startOperatorWebRTC(stream) {
operatorWebcamPeer = new RTCPeerConnection(rtcConfig);
stream.getTracks().forEach(track => operatorWebcamPeer.addTrack(track, stream));
operatorWebcamPeer.onicecandidate = e => {
if (e.candidate) {
socket.emit('webrtc:ice-candidate', { candidate: e.candidate });
}
};
operatorWebcamPeer.createOffer().then(offer => {
return operatorWebcamPeer.setLocalDescription(offer);
}).then(() => {
socket.emit('webrtc:offer', { offer: operatorWebcamPeer.localDescription });
});
}
socket.on('webrtc:answer', data => {
if (operatorWebcamPeer) {
operatorWebcamPeer.setRemoteDescription(new RTCSessionDescription(data.answer));
}
});
socket.on('webrtc:ice-candidate', data => {
if (operatorWebcamPeer && data.candidate) {
operatorWebcamPeer.addIceCandidate(new RTCIceCandidate(data.candidate));
}
});
// Показывать кнопку веб-камеры при принятии сессии
socket.on('camera:response', data => {
if (data.accepted) {
showOperatorWebcamButton();
} else {
hideOperatorWebcamButton();
}
});
}