Files
crm.clientright.ru/crm_extensions/file_storage/js/file_sync_sse.js

295 lines
12 KiB
JavaScript
Raw Normal View History

/**
* SSE (Server-Sent Events) клиент для синхронизации файлов в реальном времени
*
* Автоматически подключается к SSE endpoint и обновляет UI при изменениях файлов
*/
class FileSyncSSE {
constructor() {
this.eventSource = null;
this.reconnectInterval = 5000; // 5 секунд
this.maxReconnectAttempts = 10;
this.reconnectAttempts = 0;
this.isConnected = false;
this.init();
}
init() {
console.log('🔄 Инициализация SSE для синхронизации файлов...');
this.connect();
}
connect() {
try {
// Закрываем предыдущее соединение
if (this.eventSource) {
this.eventSource.close();
}
// Создаем новое SSE соединение
this.eventSource = new EventSource('/crm_extensions/file_storage/api/sse_events.php');
// Обработчик успешного подключения
this.eventSource.onopen = (event) => {
console.log('✅ SSE подключение установлено');
this.isConnected = true;
this.reconnectAttempts = 0;
this.showConnectionStatus('connected');
};
// Обработчик сообщений
this.eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.handleEvent(data);
} catch (error) {
console.error('❌ Ошибка парсинга SSE данных:', error);
}
};
// Обработчик ошибок
this.eventSource.onerror = (event) => {
console.error('❌ SSE ошибка:', event);
this.isConnected = false;
this.showConnectionStatus('disconnected');
// Попытка переподключения
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`🔄 Попытка переподключения ${this.reconnectAttempts}/${this.maxReconnectAttempts}...`);
setTimeout(() => {
this.connect();
}, this.reconnectInterval);
} else {
console.error('❌ Максимальное количество попыток переподключения достигнуто');
this.showConnectionStatus('failed');
}
};
} catch (error) {
console.error('❌ Ошибка создания SSE соединения:', error);
this.showConnectionStatus('error');
}
}
handleEvent(data) {
console.log('📨 SSE событие:', data);
switch (data.type) {
case 'connected':
console.log('✅ SSE подключен:', data.data.message);
break;
case 'disconnected':
console.log('❌ SSE отключен:', data.data.message);
break;
case 'heartbeat':
// Heartbeat - просто обновляем статус
break;
case 'file_created':
this.handleFileCreated(data.data);
break;
case 'file_updated':
this.handleFileUpdated(data.data);
break;
case 'file_deleted':
this.handleFileDeleted(data.data);
break;
case 'folder_renamed':
this.handleFolderRenamed(data.data);
break;
case 'folder_deleted':
this.handleFolderDeleted(data.data);
break;
default:
console.log('❓ Неизвестное SSE событие:', data.type);
}
}
handleFileCreated(data) {
console.log('📄 Файл создан:', data);
// Показываем уведомление
this.showNotification('Файл добавлен', `Файл "${data.fileName}" добавлен в ${data.module}`, 'success');
// Обновляем список файлов если мы на странице детального просмотра
this.refreshFileList(data.module, data.recordId);
}
handleFileUpdated(data) {
console.log('📝 Файл обновлен:', data);
// Показываем уведомление
this.showNotification('Файл обновлен', `Файл "${data.fileName}" обновлен в ${data.module}`, 'info');
// Обновляем список файлов
this.refreshFileList(data.module, data.recordId);
}
handleFileDeleted(data) {
console.log('🗑️ Файл удален:', data);
// Показываем уведомление
this.showNotification('Файл удален', `Файл "${data.fileName}" удален из ${data.module}`, 'warning');
// Обновляем список файлов
this.refreshFileList(data.module, data.recordId);
}
handleFolderRenamed(data) {
console.log('📁 Папка переименована:', data);
// Показываем уведомление
this.showNotification('Папка переименована', `Папка переименована в ${data.module}`, 'info');
// Обновляем список файлов
this.refreshFileList(data.module, data.recordId);
}
handleFolderDeleted(data) {
console.log('🗂️ Папка удалена:', data);
// Показываем уведомление
this.showNotification('Папка удалена', `Папка удалена из ${data.module}`, 'error');
// Обновляем список файлов
this.refreshFileList(data.module, data.recordId);
}
refreshFileList(module, recordId) {
// Проверяем, находимся ли мы на странице детального просмотра нужного модуля
const currentModule = window.location.search.match(/module=([^&]+)/);
const currentRecord = window.location.search.match(/record=([^&]+)/);
if (currentModule && currentModule[1] === module &&
currentRecord && currentRecord[1] === recordId) {
console.log('🔄 Обновляем список файлов...');
// Обновляем страницу или конкретный блок с файлами
if (typeof refreshFileList === 'function') {
refreshFileList();
} else {
// Fallback - обновляем всю страницу
setTimeout(() => {
window.location.reload();
}, 1000);
}
}
}
showNotification(title, message, type = 'info') {
// Используем существующую систему уведомлений CRM
if (typeof Vtiger_Helper_Js !== 'undefined' && Vtiger_Helper_Js.showPnotify) {
Vtiger_Helper_Js.showPnotify({
title: title,
text: message,
type: type,
delay: 5000
});
} else {
// Fallback - обычный alert
alert(`${title}: ${message}`);
}
}
showConnectionStatus(status) {
// Создаем или обновляем индикатор статуса подключения
let statusElement = document.getElementById('sse-connection-status');
if (!statusElement) {
statusElement = document.createElement('div');
statusElement.id = 'sse-connection-status';
statusElement.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
z-index: 9999;
transition: all 0.3s ease;
`;
document.body.appendChild(statusElement);
}
switch (status) {
case 'connected':
statusElement.textContent = '🟢 Файлы синхронизируются';
statusElement.style.backgroundColor = '#d4edda';
statusElement.style.color = '#155724';
statusElement.style.border = '1px solid #c3e6cb';
break;
case 'disconnected':
statusElement.textContent = '🟡 Переподключение...';
statusElement.style.backgroundColor = '#fff3cd';
statusElement.style.color = '#856404';
statusElement.style.border = '1px solid #ffeaa7';
break;
case 'failed':
statusElement.textContent = '🔴 Синхронизация недоступна';
statusElement.style.backgroundColor = '#f8d7da';
statusElement.style.color = '#721c24';
statusElement.style.border = '1px solid #f5c6cb';
break;
case 'error':
statusElement.textContent = '❌ Ошибка подключения';
statusElement.style.backgroundColor = '#f8d7da';
statusElement.style.color = '#721c24';
statusElement.style.border = '1px solid #f5c6cb';
break;
}
// Автоматически скрываем через 5 секунд для успешного подключения
if (status === 'connected') {
setTimeout(() => {
if (statusElement) {
statusElement.style.opacity = '0.7';
}
}, 5000);
}
}
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
this.isConnected = false;
console.log('🔌 SSE соединение закрыто');
}
}
// Автоматически инициализируем SSE при загрузке страницы
document.addEventListener('DOMContentLoaded', function() {
// Проверяем, что мы в CRM (не в админке или других разделах)
if (window.location.pathname.includes('/index.php') &&
!window.location.pathname.includes('/admin') &&
!window.location.pathname.includes('/install')) {
console.log('🚀 Запуск SSE синхронизации файлов...');
window.fileSyncSSE = new FileSyncSSE();
}
});
// Экспортируем для использования в других модулях
if (typeof module !== 'undefined' && module.exports) {
module.exports = FileSyncSSE;
}