UNPKG

n8n-nodes-websocket

Version:

Enhanced WebSocket nodes for n8n with bidirectional communication support

391 lines 18.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebsocketResponse = void 0; const n8n_workflow_1 = require("n8n-workflow"); const WebSocketConnections_1 = require("../Shared/WebSocketConnections"); async function handleReplyToSender(executeFunctions, itemIndex, inputData, options) { const connectionIdSource = executeFunctions.getNodeParameter('connectionIdSource', itemIndex); let connectionId; if (connectionIdSource === 'fromInput') { const connectionInfo = inputData.json.connectionInfo; const meta = inputData.json.meta; connectionId = (connectionInfo === null || connectionInfo === void 0 ? void 0 : connectionInfo.connectionId) || inputData.json.connectionId || (meta === null || meta === void 0 ? void 0 : meta.connectionId); if (!connectionId) { throw new n8n_workflow_1.NodeOperationError(executeFunctions.getNode(), 'No connectionId found in input data. Make sure this node comes after a WebSocket Trigger.', { itemIndex }); } } else { connectionId = executeFunctions.getNodeParameter('connectionId', itemIndex); } const message = await buildMessage(executeFunctions, itemIndex, inputData); if (options.verifyConnection) { const activeConnections = WebSocketConnections_1.WebSocketConnectionManager.getActiveConnections(); const connectionExists = activeConnections.some((conn) => conn.connectionId === connectionId && conn.isOpen); if (!connectionExists) { throw new n8n_workflow_1.NodeOperationError(executeFunctions.getNode(), `Connection ${connectionId} is not active or does not exist`, { itemIndex }); } } const success = WebSocketConnections_1.WebSocketConnectionManager.sendToConnection(connectionId, message); return { operation: 'replyToSender', connectionId, message, success, totalConnections: WebSocketConnections_1.WebSocketConnectionManager.getConnectionCount(), }; } async function handleSendToConnection(executeFunctions, itemIndex, inputData, options) { const connectionId = executeFunctions.getNodeParameter('connectionId', itemIndex); const message = await buildMessage(executeFunctions, itemIndex, inputData); if (options.verifyConnection) { const activeConnections = WebSocketConnections_1.WebSocketConnectionManager.getActiveConnections(); const connectionExists = activeConnections.some((conn) => conn.connectionId === connectionId && conn.isOpen); if (!connectionExists) { throw new n8n_workflow_1.NodeOperationError(executeFunctions.getNode(), `Connection ${connectionId} is not active or does not exist`, { itemIndex }); } } const success = WebSocketConnections_1.WebSocketConnectionManager.sendToConnection(connectionId, message); return { operation: 'sendToConnection', connectionId, message, success, totalConnections: WebSocketConnections_1.WebSocketConnectionManager.getConnectionCount(), }; } async function handleBroadcastToAll(executeFunctions, itemIndex, inputData, options) { const message = await buildMessage(executeFunctions, itemIndex, inputData); const sentCount = WebSocketConnections_1.WebSocketConnectionManager.broadcastToAll(message); const totalConnections = WebSocketConnections_1.WebSocketConnectionManager.getConnectionCount(); if (sentCount === 0 && !options.continueOnError) { throw new n8n_workflow_1.NodeOperationError(executeFunctions.getNode(), 'No active connections to broadcast message to', { itemIndex }); } return { operation: 'broadcastToAll', message, sentToConnections: sentCount, totalConnections, success: sentCount > 0, }; } function handleGetConnections() { const connections = WebSocketConnections_1.WebSocketConnectionManager.getActiveConnections(); return { operation: 'getConnections', totalConnections: connections.length, connections: connections.map(conn => ({ connectionId: conn.connectionId, remoteAddress: conn.connectionInfo.remoteAddress, userAgent: conn.connectionInfo.userAgent, origin: conn.connectionInfo.origin, connectedSince: conn.connectionInfo.timestamp, lastActivity: conn.lastActivity, isOpen: conn.isOpen, })), }; } async function buildMessage(executeFunctions, itemIndex, inputData) { const messageFormat = executeFunctions.getNodeParameter('messageFormat', itemIndex); switch (messageFormat) { case 'text': return executeFunctions.getNodeParameter('message', itemIndex); case 'json': const jsonMessage = executeFunctions.getNodeParameter('jsonMessage', itemIndex, {}); const message = {}; if (jsonMessage.type) message.type = jsonMessage.type; if (jsonMessage.content) message.message = jsonMessage.content; if (jsonMessage.status) message.status = jsonMessage.status; if (jsonMessage.includeTimestamp !== false) { message.timestamp = new Date().toISOString(); } return JSON.stringify(message); case 'template': let templateMessage = executeFunctions.getNodeParameter('message', itemIndex); templateMessage = WebSocketConnections_1.WebSocketConnectionManager.processMessageTemplate(templateMessage); return templateMessage; default: throw new n8n_workflow_1.NodeOperationError(executeFunctions.getNode(), `Unknown message format: ${messageFormat}`); } } class WebsocketResponse { constructor() { this.description = { displayName: 'WebSocket Response', name: 'websocketResponse', icon: 'file:websocket_icon.svg', group: ['communication'], version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Send responses to specific WebSocket connections or broadcast messages to all clients', defaults: { name: 'WebSocket Response', }, inputs: ["main"], outputs: ["main"], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'Reply to Sender', value: 'replyToSender', description: 'Send message back to the original sender (uses connectionId from input)', action: 'Reply to original sender', }, { name: 'Send to Specific Connection', value: 'sendToConnection', description: 'Send message to a specific WebSocket connection by ID', action: 'Send to specific connection', }, { name: 'Broadcast to All', value: 'broadcastToAll', description: 'Send message to all connected WebSocket clients', action: 'Broadcast to all connections', }, { name: 'Get Active Connections', value: 'getConnections', description: 'Get list of all active WebSocket connections', action: 'Get active connections', }, ], default: 'replyToSender', }, { displayName: 'Connection ID Source', name: 'connectionIdSource', type: 'options', options: [ { name: 'From Input Data', value: 'fromInput', description: 'Use connectionId from the input data (from WebSocket Trigger)', }, { name: 'Manual Input', value: 'manual', description: 'Manually specify the connection ID', }, ], default: 'fromInput', description: 'Where to get the connection ID from', displayOptions: { show: { operation: ['replyToSender', 'sendToConnection'], }, }, }, { displayName: 'Connection ID', name: 'connectionId', type: 'string', default: '', placeholder: 'abc123', description: 'ID of the WebSocket connection to send message to', required: true, displayOptions: { show: { operation: ['sendToConnection'], connectionIdSource: ['manual'], }, }, }, { displayName: 'Connection ID', name: 'connectionId', type: 'string', default: '', placeholder: 'abc123', description: 'ID of the WebSocket connection to send message to', required: true, displayOptions: { show: { operation: ['replyToSender'], connectionIdSource: ['manual'], }, }, }, { displayName: 'Message', name: 'message', type: 'string', default: '', placeholder: 'Hello from n8n workflow!', description: 'Message to send to WebSocket client(s)', required: true, displayOptions: { show: { operation: ['replyToSender', 'sendToConnection', 'broadcastToAll'], }, }, }, { displayName: 'Message Format', name: 'messageFormat', type: 'options', options: [ { name: 'Plain Text', value: 'text', description: 'Send message as plain text', }, { name: 'JSON Object', value: 'json', description: 'Build and send a JSON object', }, { name: 'Template with Variables', value: 'template', description: 'Use template with input data variables', }, ], default: 'text', description: 'Format of the message to send', displayOptions: { show: { operation: ['replyToSender', 'sendToConnection', 'broadcastToAll'], }, }, }, { displayName: 'JSON Message Builder', name: 'jsonMessage', type: 'collection', placeholder: 'Add Field', default: {}, options: [ { displayName: 'Message Type', name: 'type', type: 'string', default: 'response', placeholder: 'response', description: 'Type of the message', }, { displayName: 'Message Content', name: 'content', type: 'string', default: '', placeholder: 'Response from workflow', description: 'Main message content', }, { displayName: 'Status', name: 'status', type: 'options', options: [ { name: 'Success', value: 'success' }, { name: 'Error', value: 'error' }, { name: 'Info', value: 'info' }, { name: 'Warning', value: 'warning' }, ], default: 'success', description: 'Status of the response', }, { displayName: 'Include Timestamp', name: 'includeTimestamp', type: 'boolean', default: true, description: 'Whether to include timestamp in the message', }, ], displayOptions: { show: { operation: ['replyToSender', 'sendToConnection', 'broadcastToAll'], messageFormat: ['json'], }, }, }, { displayName: 'Response Options', name: 'responseOptions', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Continue on Send Error', name: 'continueOnError', type: 'boolean', default: false, description: 'Continue execution even if sending fails', }, { displayName: 'Verify Connection Before Send', name: 'verifyConnection', type: 'boolean', default: true, description: 'Check if connection is still active before sending', }, ], }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const operation = this.getNodeParameter('operation', i); const responseOptions = this.getNodeParameter('responseOptions', i, {}); let result = {}; switch (operation) { case 'replyToSender': result = await handleReplyToSender(this, i, items[i], responseOptions); break; case 'sendToConnection': result = await handleSendToConnection(this, i, items[i], responseOptions); break; case 'broadcastToAll': result = await handleBroadcastToAll(this, i, items[i], responseOptions); break; case 'getConnections': result = handleGetConnections(); break; default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, { itemIndex: i, }); } result.originalInput = items[i].json; result.itemIndex = i; result.timestamp = new Date().toISOString(); returnData.push({ json: result }); } catch (error) { const responseOptions = this.getNodeParameter('responseOptions', i, {}); if (responseOptions.continueOnError) { returnData.push({ json: { error: error.message, success: false, operation: this.getNodeParameter('operation', i), timestamp: new Date().toISOString(), originalInput: items[i].json, }, }); } else { throw error; } } } return [returnData]; } } exports.WebsocketResponse = WebsocketResponse; //# sourceMappingURL=WebsocketResponse.node.js.map