Files
SimpleClient-releases/socketio-adapter.js
Pierre Marx a6b33bf3cb fix: annuler le timeout logoff quand logout_ok arrive
clearTimeout sur le fallback 5s pour éviter un disconnect()
parasite sur un socket déjà fermé.
2026-03-19 12:15:34 -04:00

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;