clearTimeout sur le fallback 5s pour éviter un disconnect() parasite sur un socket déjà fermé.
187 lines
4.9 KiB
JavaScript
187 lines
4.9 KiB
JavaScript
/**
|
|
* Adaptateur Socket.IO natif pour le serveur Python SimpleServer
|
|
*
|
|
* Connexion directe au port 8004, auth au handshake,
|
|
* reconnexion native illimitee.
|
|
*/
|
|
|
|
const io = require('socket.io-client');
|
|
|
|
class SocketIOAdapter {
|
|
constructor(serverUrl, socketFactory = null) {
|
|
this.serverUrl = serverUrl;
|
|
this._socketFactory = socketFactory || io;
|
|
this.socket = null;
|
|
this._state = 'disconnected'; // disconnected, connecting, connected, reconnecting, error
|
|
this._eventHandlers = new Map();
|
|
this._onStateChange = null; // callback pour notifier main.js des changements d'etat
|
|
}
|
|
|
|
/**
|
|
* Enregistrer un callback de changement d'etat.
|
|
* @param {function(string)} callback - recoit le nouvel etat
|
|
*/
|
|
onStateChange(callback) {
|
|
this._onStateChange = callback;
|
|
}
|
|
|
|
_setState(state) {
|
|
this._state = state;
|
|
if (this._onStateChange) {
|
|
this._onStateChange(state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Connexion avec auth au handshake.
|
|
* Le serveur authentifie dans le handler connect et emet 'login_ok' avec le resultat.
|
|
* @returns {Promise<object>} connResult (accessCode, firstName, lastName, connList)
|
|
*/
|
|
connect(accessCode, password, terminal) {
|
|
return new Promise((resolve, reject) => {
|
|
this._setState('connecting');
|
|
|
|
this.socket = this._socketFactory(this.serverUrl, {
|
|
auth: { access_code: accessCode, password, terminal },
|
|
transports: ['websocket'],
|
|
reconnection: true,
|
|
reconnectionAttempts: Infinity,
|
|
reconnectionDelay: 2000,
|
|
reconnectionDelayMax: 10000,
|
|
});
|
|
|
|
let settled = false;
|
|
|
|
// Le serveur emet 'login_ok' avec les donnees de session
|
|
this.socket.once('login_ok', (data) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
this._setState('connected');
|
|
resolve(data);
|
|
});
|
|
|
|
// Le serveur emet 'login_error' si auth echouee (avant return false)
|
|
this.socket.once('login_error', (data) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
this._setState('error');
|
|
this.socket.disconnect();
|
|
reject(new Error(data.message || 'Authentification refusee'));
|
|
});
|
|
|
|
// Erreur de connexion (serveur injoignable ou return false du handler connect)
|
|
this.socket.on('connect_error', (err) => {
|
|
if (!settled) {
|
|
settled = true;
|
|
this._setState('error');
|
|
this.socket.disconnect();
|
|
reject(new Error(err.message || 'Connexion refusee'));
|
|
}
|
|
// Apres login reussi : reconnexion auto, on passe en 'reconnecting'
|
|
});
|
|
|
|
// Timeout de connexion initiale (15s)
|
|
setTimeout(() => {
|
|
if (!settled) {
|
|
settled = true;
|
|
this._setState('error');
|
|
this.socket.disconnect();
|
|
reject(new Error('Timeout de connexion au serveur'));
|
|
}
|
|
}, 15000);
|
|
|
|
// === Handlers de reconnexion (actifs apres le premier login) ===
|
|
|
|
// Deconnexion temporaire → passer en 'reconnecting'
|
|
this.socket.on('disconnect', (reason) => {
|
|
if (this._state === 'connected') {
|
|
// Deconnexion temporaire, socket.io va retenter
|
|
this._setState('reconnecting');
|
|
}
|
|
});
|
|
|
|
// Reconnexion reussie → le serveur recoit un nouveau connect avec les memes auth
|
|
// et emet 'login_ok' (reprise de session)
|
|
this.socket.on('login_ok', (data) => {
|
|
if (settled && this._state === 'reconnecting') {
|
|
this._setState('connected');
|
|
}
|
|
});
|
|
|
|
// Restaurer les handlers enregistres avant connect
|
|
this._eventHandlers.forEach((handler, event) => {
|
|
this.socket.on(event, handler);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Deconnexion volontaire avec logoff IPBX.
|
|
* Emet 'logout' et attend 'logout_ok' du serveur.
|
|
*/
|
|
logoff() {
|
|
return new Promise((resolve) => {
|
|
if (!this.socket || !this.socket.connected) {
|
|
this._setState('disconnected');
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
const timeout = setTimeout(() => {
|
|
if (this.socket) {
|
|
this.socket.disconnect();
|
|
}
|
|
this._setState('disconnected');
|
|
resolve();
|
|
}, 5000);
|
|
|
|
this.socket.once('logout_ok', () => {
|
|
clearTimeout(timeout);
|
|
this._setState('disconnected');
|
|
resolve();
|
|
});
|
|
|
|
this.socket.emit('logout');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Deconnexion brute (sans logoff IPBX).
|
|
*/
|
|
disconnect() {
|
|
if (this.socket) {
|
|
this.socket.disconnect();
|
|
}
|
|
this._setState('disconnected');
|
|
}
|
|
|
|
/**
|
|
* Ecouter un evenement serveur.
|
|
*/
|
|
on(event, handler) {
|
|
this._eventHandlers.set(event, handler);
|
|
if (this.socket) {
|
|
this.socket.on(event, handler);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retirer un handler d'evenement.
|
|
*/
|
|
off(event) {
|
|
this._eventHandlers.delete(event);
|
|
if (this.socket) {
|
|
this.socket.off(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Etat de la connexion.
|
|
*/
|
|
get state() {
|
|
return this._state;
|
|
}
|
|
}
|
|
|
|
module.exports = SocketIOAdapter;
|