n8n-nodes-websocket
Version:
Enhanced WebSocket nodes for n8n with bidirectional communication support
391 lines • 18.1 kB
JavaScript
;
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