/** * Gestionnaire de connexion avec fallback SignalR → REST + Socket.IO * * 1. Essaie d'abord SignalR (serveur .NET legacy) * 2. Si SignalR échoue, bascule sur REST + Socket.IO (serveur Python) */ const signalR = require('@microsoft/signalr'); const RestSocketAdapter = require('./rest-socket-adapter'); class ConnectionManager { constructor(serverUrl, options = {}) { this.serverUrl = serverUrl; this.options = options; this.connection = null; this.connectionType = 'none'; // 'none', 'SignalR', 'REST+SocketIO' } /** * Établit la connexion en essayant SignalR puis REST + Socket.IO */ async connect() { console.log('🔌 Tentative de connexion au serveur...'); // 1. Essayer SignalR d'abord (serveur .NET) try { console.log('📡 Essai avec SignalR...'); this.connection = this._createSignalRConnection(); await this.connection.start(); this.connectionType = 'SignalR'; console.log('✅ Connexion SignalR établie (serveur .NET)'); return this.connection; } catch (signalRError) { console.warn('⚠️ SignalR indisponible:', signalRError.message); console.log('🔄 Basculement vers REST + Socket.IO...'); } // 2. Fallback vers REST + Socket.IO (serveur Python) try { this.connection = new RestSocketAdapter(this.serverUrl, this.options); await this.connection.start(); this.connectionType = 'REST+SocketIO'; console.log('✅ Connexion REST + Socket.IO établie (serveur Python)'); return this.connection; } catch (restError) { console.error('❌ REST + Socket.IO indisponible:', restError.message); throw new Error('Impossible de se connecter (SignalR et REST+SocketIO ont échoué)'); } } /** * Crée une connexion SignalR */ _createSignalRConnection() { let url = this.serverUrl; if (!url.startsWith('http://') && !url.startsWith('https://')) { url = `http://${url}`; } if (!url.includes('/signalR')) { url = `${url}/signalR`; } console.log(`📡 URL SignalR: ${url}`); return new signalR.HubConnectionBuilder() .withUrl(url) .withAutomaticReconnect([0, 2000, 5000, 10000]) .configureLogging(signalR.LogLevel.Information) .build(); } /** * Retourne la connexion active */ getConnection() { return this.connection; } /** * Déconnecte proprement */ async disconnect() { if (this.connection) { await this.connection.stop(); this.connection = null; this.connectionType = 'none'; } } /** * Informations sur la connexion */ getConnectionInfo() { return { type: this.connectionType, isSignalR: this.connectionType === 'SignalR', isRestSocketIO: this.connectionType === 'REST+SocketIO', isConnected: this.connection && this.connection.state === 'Connected', serverUrl: this.serverUrl }; } /** * Méthode helper pour invoquer des méthodes sur la connexion */ async invoke(methodName, ...args) { if (!this.connection) { throw new Error('Pas de connexion active'); } return this.connection.invoke(methodName, ...args); } /** * Méthode helper pour écouter des événements */ on(eventName, handler) { if (!this.connection) { throw new Error('Pas de connexion active'); } this.connection.on(eventName, handler); } /** * Méthode helper pour retirer un handler d'événement */ off(eventName) { if (this.connection && this.connection.off) { this.connection.off(eventName); } } } module.exports = ConnectionManager;