const { app, BrowserWindow, ipcMain, session } = require('electron'); const path = require('path'); const fs = require('fs'); const signalR = require('@microsoft/signalr'); let mainWindow; let config; let currentAgent = null; let signalRConnection = null; let signalRStatus = 'disconnected'; // disconnected, connecting, connected, error // Charger la configuration function loadConfig() { const configPath = path.join(__dirname, 'config.json'); const configData = fs.readFileSync(configPath, 'utf8'); config = JSON.parse(configData); } // Créer la fenêtre principale function createWindow() { mainWindow = new BrowserWindow({ width: 1400, height: 900, webPreferences: { nodeIntegration: true, contextIsolation: false, webviewTag: true, webSecurity: false }, icon: path.join(__dirname, 'icon.png'), title: 'SimpleConnect - Gestion Centralisée des Plannings' }); // Charger l'interface HTML mainWindow.loadFile('index.html'); // Ouvrir les DevTools uniquement en mode développement if (process.env.NODE_ENV === 'development') { mainWindow.webContents.openDevTools(); } // Gérer la fermeture de la fenêtre mainWindow.on('closed', () => { mainWindow = null; }); } // === GESTION SIGNALR === function initializeSignalR() { if (!config.signalR || !config.signalR.enabled) { console.log('SignalR désactivé dans la configuration'); signalRStatus = 'disabled'; sendSignalRStatus(); return; } try { // Créer la connexion SignalR signalRConnection = new signalR.HubConnectionBuilder() .withUrl(config.signalR.serverUrl) .withAutomaticReconnect([0, 2000, 5000, 10000, 30000]) .configureLogging(signalR.LogLevel.Information) .build(); // Gérer les changements d'état signalRConnection.onreconnecting(() => { console.log('SignalR: Reconnexion en cours...'); signalRStatus = 'connecting'; sendSignalRStatus(); }); signalRConnection.onreconnected(() => { console.log('SignalR: Reconnecté'); signalRStatus = 'connected'; sendSignalRStatus(); }); signalRConnection.onclose(() => { console.log('SignalR: Connexion fermée'); signalRStatus = 'disconnected'; sendSignalRStatus(); }); // Configurer les méthodes SignalR setupSignalRMethods(); // Démarrer la connexion startSignalRConnection(); } catch (error) { console.error('Erreur initialisation SignalR:', error); signalRStatus = 'error'; sendSignalRStatus(); } } function setupSignalRMethods() { // Écouter les événements IPBX signalRConnection.on('IpbxEvent', (name, args) => { if (!args) return; const event = args[0]; console.log('Événement IPBX reçu:', event); // TODO: Gérer les événements d'appel }); } async function startSignalRConnection() { try { signalRStatus = 'connecting'; sendSignalRStatus(); await signalRConnection.start(); console.log('SignalR: Connexion établie'); signalRStatus = 'connected'; sendSignalRStatus(); } catch (error) { console.error('Erreur connexion SignalR:', error); signalRStatus = 'error'; sendSignalRStatus(); // Réessayer dans 5 secondes setTimeout(() => { if (signalRConnection && signalRStatus !== 'connected') { startSignalRConnection(); } }, 5000); } } function sendSignalRStatus() { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('signalr-status', signalRStatus); } } // Initialisation de l'application app.whenReady().then(() => { // Configuration de la session pour éviter les problèmes CORS session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => { details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; callback({ requestHeaders: details.requestHeaders }); }); session.defaultSession.webRequest.onHeadersReceived((details, callback) => { callback({ responseHeaders: { ...details.responseHeaders, 'Content-Security-Policy': ['default-src * \'unsafe-inline\' \'unsafe-eval\' data: blob:;'] } }); }); loadConfig(); createWindow(); // Initialiser SignalR après le chargement de la config initializeSignalR(); }); // Quitter quand toutes les fenêtres sont fermées app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); // Réactiver l'app sur macOS app.on('activate', () => { if (mainWindow === null) { createWindow(); } }); // === IPC HANDLERS === // Obtenir la configuration ipcMain.handle('get-config', () => { return config; }); // Obtenir le statut SignalR ipcMain.handle('get-signalr-status', () => { return signalRStatus; }); // Récupérer la liste des terminaux téléphoniques ipcMain.handle('get-terminal-list', async () => { // Mode simulation si SignalR non connecté if (!signalRConnection || signalRStatus !== 'connected') { console.log('SignalR non connecté, utilisation des terminaux de simulation'); return config.signalR.terminalsSimulation || ['3001', '3002', '3003']; } try { console.log('Récupération des terminaux pour:', config.signalR.serviceProvider); const terminals = await signalRConnection.invoke( 'GetTerminalListByServiceProvider', config.signalR.serviceProvider ); console.log('Terminaux disponibles:', terminals); return terminals || []; } catch (error) { console.error('Erreur récupération terminaux:', error); // Retourner les terminaux de simulation en cas d'erreur return config.signalR.terminalsSimulation || ['3001', '3002', '3003']; } }); // Connexion agent ipcMain.handle('login-agent', (event, credentials) => { const agent = config.agents.find(a => a.email === credentials.email && a.password === credentials.password ); if (agent) { currentAgent = agent; // Retourner l'agent avec ses centres assignés const centresAssignes = config.centres.filter(c => agent.centresAssignes.includes(c.id) ); return { success: true, agent: agent, centres: centresAssignes }; } return { success: false, message: 'Email ou mot de passe incorrect' }; }); // Déconnexion ipcMain.handle('logout', () => { currentAgent = null; return { success: true }; }); // Obtenir l'agent actuel ipcMain.handle('get-current-agent', () => { if (!currentAgent) return null; const centresAssignes = config.centres.filter(c => currentAgent.centresAssignes.includes(c.id) ); return { agent: currentAgent, centres: centresAssignes }; }); // Simuler un appel entrant ipcMain.handle('simulate-call', (event, callData) => { // Envoyer l'événement d'appel entrant à la fenêtre mainWindow.webContents.send('incoming-call', callData); return { success: true }; }); // Obtenir les données pour simuler des appels ipcMain.handle('get-simulated-calls', () => { return config.cti.appelSimules.map(appel => { const centre = config.centres.find(c => c.id === appel.centreId); return { ...appel, centreNom: centre ? centre.nom : 'Centre inconnu' }; }); }); // Sauvegarder les notes de l'agent ipcMain.handle('save-notes', (event, noteData) => { const notesDir = path.join(__dirname, 'notes'); if (!fs.existsSync(notesDir)) { fs.mkdirSync(notesDir); } const fileName = `notes_${currentAgent.id}_${Date.now()}.json`; const filePath = path.join(notesDir, fileName); fs.writeFileSync(filePath, JSON.stringify({ agent: currentAgent.id, timestamp: new Date().toISOString(), ...noteData }, null, 2)); return { success: true, file: fileName }; }); // Obtenir l'historique des appels ipcMain.handle('get-call-history', () => { const historyFile = path.join(__dirname, 'call_history.json'); if (fs.existsSync(historyFile)) { const data = fs.readFileSync(historyFile, 'utf8'); return JSON.parse(data); } return []; }); // Sauvegarder un appel dans l'historique ipcMain.handle('save-call-history', (event, callData) => { const historyFile = path.join(__dirname, 'call_history.json'); let history = []; if (fs.existsSync(historyFile)) { const data = fs.readFileSync(historyFile, 'utf8'); history = JSON.parse(data); } history.unshift({ ...callData, agentId: currentAgent?.id, timestamp: new Date().toISOString() }); // Garder seulement les 100 derniers appels history = history.slice(0, 100); fs.writeFileSync(historyFile, JSON.stringify(history, null, 2)); return { success: true }; }); // Vérifier si on est en mode développement ipcMain.handle('is-development', () => { return process.env.NODE_ENV === 'development'; });