/** * Adaptateur WebSocket pour fallback quand SignalR n'est pas disponible * Émule l'API SignalR avec SocketIO */ const io = require('socket.io-client'); class WebSocketAdapter { constructor(serverUrl, options = {}) { // Nettoyer l'URL et s'assurer qu'elle est valide this.serverUrl = serverUrl; if (this.serverUrl.includes('/signalR')) { this.serverUrl = this.serverUrl.replace('/signalR', ''); } // S'assurer qu'on a le protocole if (!this.serverUrl.startsWith('http://') && !this.serverUrl.startsWith('https://')) { this.serverUrl = `http://${this.serverUrl}`; } this.socket = null; this.connected = false; this.handlers = new Map(); this.options = options; this.pendingInvocations = new Map(); this.invocationId = 0; } /** * Émule connection.start() de SignalR */ async start() { return new Promise((resolve, reject) => { console.log(`🔄 Connexion WebSocket à ${this.serverUrl}...`); // Se connecter avec SocketIO this.socket = io(this.serverUrl, { transports: ['websocket'], reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 2000, ...this.options }); this.socket.on('connect', () => { console.log('✅ WebSocket connecté (mode fallback)'); this.connected = true; // Émuler l'événement 'connected' de SignalR if (this.handlers.has('connected')) { this.handlers.get('connected')({ connectionId: this.socket.id }); } resolve(); }); this.socket.on('connect_error', (error) => { console.error('❌ Erreur connexion WebSocket:', error.message); reject(error); }); this.socket.on('disconnect', (reason) => { console.log('🔌 WebSocket déconnecté:', reason); this.connected = false; }); // Timeout de connexion setTimeout(() => { if (!this.connected) { reject(new Error('Timeout de connexion WebSocket')); } }, 10000); }); } /** * Émule connection.stop() de SignalR */ async stop() { if (this.socket) { this.socket.disconnect(); this.connected = false; console.log('🛑 WebSocket fermé'); } } /** * Émule connection.invoke() de SignalR */ async invoke(methodName, ...args) { return new Promise((resolve, reject) => { if (!this.connected) { reject(new Error('WebSocket non connecté')); return; } const invocationId = ++this.invocationId; console.log(`📤 WebSocket invoke: ${methodName}`, args); // Stocker la promesse pour la résolution this.pendingInvocations.set(invocationId, { resolve, reject }); // Émuler le format SignalR mais utiliser l'API SocketIO // SocketIO utilise emit avec un callback pour les réponses this.socket.emit(methodName, ...args, (response) => { console.log(`📥 WebSocket response for ${methodName}:`, response); const pending = this.pendingInvocations.get(invocationId); if (pending) { pending.resolve(response); this.pendingInvocations.delete(invocationId); } }); // Timeout pour éviter les promesses pendantes setTimeout(() => { const pending = this.pendingInvocations.get(invocationId); if (pending) { pending.reject(new Error(`Timeout invoking ${methodName}`)); this.pendingInvocations.delete(invocationId); } }, 30000); }); } /** * Émule connection.on() de SignalR */ on(eventName, handler) { console.log(`👂 WebSocket listener ajouté: ${eventName}`); this.handlers.set(eventName, handler); // Mapper les événements SocketIO vers les handlers if (this.socket) { this.socket.on(eventName, (...args) => { console.log(`📨 WebSocket event reçu: ${eventName}`, args); handler(eventName, args); }); } } /** * Émule connection.off() de SignalR */ off(eventName) { this.handlers.delete(eventName); if (this.socket) { this.socket.off(eventName); } } /** * État de la connexion (émule SignalR HubConnectionState) */ get state() { if (this.connected) { return 'Connected'; } return 'Disconnected'; } /** * Vérifier si la connexion utilise WebSocket (toujours vrai pour cet adaptateur) */ get isWebSocketFallback() { return true; } } module.exports = WebSocketAdapter;