feat: add REST + Socket.IO fallback for Python server

Add RestSocketAdapter that uses:
- REST API for actions (login, logout, terminals)
- Socket.IO for real-time events (IpbxEvent)

ConnectionManager now tries SignalR first (.NET server),
then falls back to REST+SocketIO (Python server).

This enables the client to work with both servers during migration.
This commit is contained in:
Pierre Marx
2025-11-24 16:05:30 -05:00
parent 60bad93f1e
commit 2258013394
3 changed files with 329 additions and 27 deletions

View File

@@ -1,61 +1,67 @@
/**
* Gestionnaire de connexion avec fallback SignalR → WebSocket
* Essaie d'abord SignalR, puis bascule sur WebSocket si nécessaire
* 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 WebSocketAdapter = require('./websocket-adapter');
const RestSocketAdapter = require('./rest-socket-adapter');
class ConnectionManager {
constructor(serverUrl, options = {}) {
this.serverUrl = serverUrl;
this.options = options;
this.connection = null;
this.isUsingWebSocketFallback = false;
this.connectionType = 'none';
this.connectionType = 'none'; // 'none', 'SignalR', 'REST+SocketIO'
}
/**
* Établit la connexion en essayant SignalR puis WebSocket
* Établit la connexion en essayant SignalR puis REST + Socket.IO
*/
async connect() {
console.log('🔌 Tentative de connexion au serveur...');
// 1. Essayer SignalR d'abord
// 1. Essayer SignalR d'abord (serveur .NET)
try {
console.log('📡 Essai avec SignalR...');
this.connection = await this.createSignalRConnection();
this.connection = this._createSignalRConnection();
await this.connection.start();
this.connectionType = 'SignalR';
this.isUsingWebSocketFallback = false;
console.log('✅ Connexion SignalR établie');
console.log('✅ Connexion SignalR établie (serveur .NET)');
return this.connection;
} catch (signalRError) {
console.warn('⚠️ SignalR indisponible:', signalRError.message);
console.log('🔄 Basculement vers WebSocket...');
console.log('🔄 Basculement vers REST + Socket.IO...');
}
// 2. Fallback vers WebSocket
// 2. Fallback vers REST + Socket.IO (serveur Python)
try {
this.connection = new WebSocketAdapter(this.serverUrl, this.options);
this.connection = new RestSocketAdapter(this.serverUrl, this.options);
await this.connection.start();
this.connectionType = 'WebSocket';
this.isUsingWebSocketFallback = true;
console.log('✅ Connexion WebSocket établie (mode fallback)');
this.connectionType = 'REST+SocketIO';
console.log('✅ Connexion REST + Socket.IO établie (serveur Python)');
return this.connection;
} catch (wsError) {
console.error('❌ Impossible de se connecter (SignalR et WebSocket ont échoué)');
throw wsError;
} 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() {
const url = `http://${this.serverUrl}/signalR`;
console.log(`Creating SignalR connection to: ${url}`);
_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])
@@ -87,7 +93,8 @@ class ConnectionManager {
getConnectionInfo() {
return {
type: this.connectionType,
isWebSocketFallback: this.isUsingWebSocketFallback,
isSignalR: this.connectionType === 'SignalR',
isRestSocketIO: this.connectionType === 'REST+SocketIO',
isConnected: this.connection && this.connection.state === 'Connected',
serverUrl: this.serverUrl
};
@@ -112,6 +119,15 @@ class ConnectionManager {
}
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;
module.exports = ConnectionManager;