// services/daemonWebSocketBridge.js import WebSocket from 'ws'; import { getIo } from '../utils/socket.js'; class DaemonWebSocketBridge { constructor() { this.daemonSocket = null; this.reconnectInterval = 5000; this.maxReconnectAttempts = 10; this.reconnectAttempts = 0; this.isConnected = false; } connect() { if (this.daemonSocket && this.daemonSocket.readyState === WebSocket.OPEN) { console.log('πŸ”Œ Daemon WebSocket bereits verbunden'); return; } const daemonUrl = process.env.DAEMON_WS_URL || 'wss://www.your-part.de:4551'; console.log('πŸ”Œ Verbinde mit Daemon WebSocket:', daemonUrl); try { this.daemonSocket = new WebSocket(daemonUrl); this.daemonSocket.on('open', () => { console.log('βœ… Daemon WebSocket verbunden'); console.log('πŸ” Daemon URL:', daemonUrl); console.log('πŸ” ReadyState:', this.daemonSocket.readyState); console.log('πŸ” Protocol:', this.daemonSocket.protocol); console.log('πŸ” Extensions:', this.daemonSocket.extensions); this.isConnected = true; this.reconnectAttempts = 0; // Registriere uns beim Daemon console.log('πŸ“€ Registriere Backend beim Daemon...'); this.send({ event: 'register_backend', data: { type: 'backend_bridge' } }); }); this.daemonSocket.on('message', (data) => { const rawMessage = data.toString(); console.log('='.repeat(80)); console.log('πŸ“¨ DAEMON β†’ BACKEND'); console.log('πŸ“¨ Raw Message:', rawMessage); console.log('πŸ“¨ LΓ€nge:', rawMessage.length); console.log('πŸ“¨ Zeitstempel:', new Date().toISOString()); // Ignoriere ping/pong-Nachrichten if (rawMessage === 'ping' || rawMessage === 'pong') { console.log('πŸ“ Ping/Pong-Nachricht ignoriert'); console.log('='.repeat(80)); return; } try { const message = JSON.parse(rawMessage); console.log('βœ… JSON erfolgreich geparst'); console.log('πŸ“¨ Parsed Message:', JSON.stringify(message, null, 2)); this.handleDaemonMessage(message); } catch (error) { console.error('❌ JSON Parse Fehler:'); console.error('❌ Error:', error.message); console.error('❌ Raw data:', rawMessage); console.error('❌ Hex:', Buffer.from(rawMessage, 'utf8').toString('hex')); console.log('='.repeat(80)); } }); this.daemonSocket.on('close', (code, reason) => { console.log('❌ Daemon WebSocket getrennt:', code, reason.toString()); this.isConnected = false; this.scheduleReconnect(); }); this.daemonSocket.on('error', (error) => { console.error('❌ Daemon WebSocket Fehler:', error.message); this.isConnected = false; }); } catch (error) { console.error('❌ Fehler beim Erstellen der Daemon WebSocket-Verbindung:', error); this.scheduleReconnect(); } } handleDaemonMessage(message) { console.log('πŸ” BACKEND VERARBEITUNG'); console.log('πŸ” Message Type:', typeof message); console.log('πŸ” Message Keys:', Object.keys(message || {})); // PrΓΌfe verschiedene Nachrichtenformate let eventName = null; let eventData = {}; if (message.event) { // Standard-Format: { event: 'eventName', data: {...} } console.log('πŸ” Format: Standard (event)'); eventName = message.event; eventData = message.data || {}; } else if (message.type) { // Alternative Format: { type: 'eventName', ... } console.log('πŸ” Format: Alternative (type)'); eventName = message.type; eventData = { ...message }; delete eventData.type; } else if (typeof message === 'string') { // String-Format: 'eventName' console.log('πŸ” Format: String'); eventName = message; } else if (Array.isArray(message)) { // Array-Format: ['eventName', {...}] console.log('πŸ” Format: Array'); eventName = message[0]; eventData = message[1] || {}; } else { console.log('⚠️ Format: Unbekannt'); eventName = 'unknown_event'; eventData = message; } console.log('πŸ” Extracted Event:', eventName); console.log('πŸ” Extracted Data:', eventData); // Leite spezifische Events an Socket.io weiter if (eventName) { const io = getIo(); console.log('πŸ“€ BACKEND β†’ FRONTEND'); console.log('πŸ“€ Socket.io verfΓΌgbar:', !!io); console.log('πŸ“€ Event Name:', eventName); console.log('πŸ“€ Event Data:', eventData); switch (eventName) { case 'production_ready': case 'stock_change': case 'price_update': case 'director_death': case 'production_started': case 'selled_items': case 'falukantUpdateStatus': case 'falukantBranchUpdate': case 'knowledge_update': case 'familychanged': // Broadcast an alle verbundenen Clients console.log(`πŸ“€ Sende Event "${eventName}" an alle Socket.io Clients...`); if (io) { io.emit(eventName, eventData); console.log(`βœ… Event "${eventName}" erfolgreich an Socket.io gesendet`); console.log(`βœ… Data:`, JSON.stringify(eventData, null, 2)); } else { console.error('❌ Socket.io nicht verfΓΌgbar!'); } break; case 'workerStatus': // Spezielle Behandlung fΓΌr Worker-Status console.log(`πŸ“€ Sende Worker-Status Event an alle Socket.io Clients...`); if (io) { io.emit('daemon_worker_status', eventData); console.log(`βœ… Worker-Status erfolgreich an Socket.io gesendet`); console.log(`βœ… Data:`, JSON.stringify(eventData, null, 2)); } else { console.error('❌ Socket.io nicht verfΓΌgbar!'); } break; default: console.log('⚠️ Unbekanntes Daemon-Event:', eventName); console.log('⚠️ Event wird nicht weitergeleitet'); } } else { console.log('⚠️ Kein Event-Name extrahiert - keine Weiterleitung'); } console.log('='.repeat(80)); } send(data) { console.log('πŸ“€ BACKEND β†’ DAEMON'); console.log('πŸ“€ Data:', JSON.stringify(data, null, 2)); console.log('πŸ“€ WebSocket Status:', this.daemonSocket ? this.daemonSocket.readyState : 'CLOSED'); if (this.daemonSocket && this.daemonSocket.readyState === WebSocket.OPEN) { const message = JSON.stringify(data); console.log('πŸ“€ Sende an Daemon:', message); this.daemonSocket.send(message); console.log('βœ… Nachricht erfolgreich an Daemon gesendet'); } else { console.warn('⚠️ Daemon WebSocket nicht verbunden - kann Nachricht nicht senden'); console.warn('⚠️ ReadyState:', this.daemonSocket ? this.daemonSocket.readyState : 'CLOSED'); } console.log('='.repeat(80)); } scheduleReconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('❌ Maximale Wiederverbindungsversuche erreicht'); return; } this.reconnectAttempts++; const delay = Math.min(this.reconnectInterval * this.reconnectAttempts, 30000); console.log(`πŸ”„ Wiederverbindung in ${delay}ms (Versuch ${this.reconnectAttempts}/${this.maxReconnectAttempts})`); setTimeout(() => { this.connect(); }, delay); } disconnect() { if (this.daemonSocket) { this.daemonSocket.close(); this.daemonSocket = null; this.isConnected = false; } } getStatus() { return { connected: this.isConnected, reconnectAttempts: this.reconnectAttempts, readyState: this.daemonSocket ? this.daemonSocket.readyState : 'CLOSED' }; } } // Singleton-Instanz const daemonBridge = new DaemonWebSocketBridge(); export default daemonBridge;