UNPKG

@soketi/soketi

Version:

Just another simple, fast, and resilient open-source WebSockets server.

226 lines (225 loc) 9.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebhookSender = exports.createWebhookHmac = void 0; const app_1 = require("./app"); const async_1 = require("async"); const axios_1 = require("axios"); const crypto_1 = require("crypto"); const utils_1 = require("./utils"); const aws_sdk_1 = require("aws-sdk"); const log_1 = require("./log"); function createWebhookHmac(data, secret) { return (0, crypto_1.createHmac)('sha256', secret) .update(data) .digest('hex'); } exports.createWebhookHmac = createWebhookHmac; class WebhookSender { constructor(server) { this.server = server; this.batch = []; this.batchHasLeader = false; let queueProcessor = (job, done) => { let rawData = job.data; const { appKey, payload, originalPusherSignature } = rawData; server.appManager.findByKey(appKey).then(app => { if (originalPusherSignature !== createWebhookHmac(JSON.stringify(payload), app.secret)) { return; } async_1.default.each(app.webhooks, (webhook, resolveWebhook) => { var _a; const originalEventsLength = payload.events.length; let filteredPayloadEvents = payload.events; filteredPayloadEvents = filteredPayloadEvents.filter(event => { if (!webhook.event_types.includes(event.name)) { return false; } if (webhook.filter) { if (webhook.filter.channel_name_starts_with && !event.channel.startsWith(webhook.filter.channel_name_starts_with)) { return false; } if (webhook.filter.channel_name_ends_with && !event.channel.endsWith(webhook.filter.channel_name_ends_with)) { return false; } } return true; }); if (filteredPayloadEvents.length === 0) { return resolveWebhook(); } let pusherSignature = (originalEventsLength !== filteredPayloadEvents.length) ? createWebhookHmac(JSON.stringify(payload), app.secret) : originalPusherSignature; if (this.server.options.debug) { log_1.Log.webhookSenderTitle('🚀 Processing webhook from queue.'); log_1.Log.webhookSender({ appKey, payload, pusherSignature }); } const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'User-Agent': `SoketiWebhooksAxiosClient/1.0 (Process: ${this.server.options.instance.process_id})`, ...(_a = webhook.headers) !== null && _a !== void 0 ? _a : {}, 'X-Pusher-Key': appKey, 'X-Pusher-Signature': pusherSignature, }; if (webhook.url) { axios_1.default.post(webhook.url, payload, { headers }).then((res) => { if (this.server.options.debug) { log_1.Log.webhookSenderTitle('✅ Webhook sent.'); log_1.Log.webhookSender({ webhook, payload }); } }).catch(err => { if (this.server.options.debug) { log_1.Log.webhookSenderTitle('❎ Webhook could not be sent.'); log_1.Log.webhookSender({ err, webhook, payload }); } }).then(() => resolveWebhook()); } else if (webhook.lambda_function) { const params = { FunctionName: webhook.lambda_function, InvocationType: webhook.lambda.async ? 'Event' : 'RequestResponse', Payload: Buffer.from(JSON.stringify({ payload, headers })), }; let lambda = new aws_sdk_1.Lambda({ apiVersion: '2015-03-31', region: webhook.lambda.region || 'us-east-1', ...(webhook.lambda.client_options || {}), }); lambda.invoke(params, (err, data) => { if (err) { if (this.server.options.debug) { log_1.Log.webhookSenderTitle('❎ Lambda trigger failed.'); log_1.Log.webhookSender({ webhook, err, data }); } } else { if (this.server.options.debug) { log_1.Log.webhookSenderTitle('✅ Lambda triggered.'); log_1.Log.webhookSender({ webhook, payload }); } } resolveWebhook(); }); } }).then(() => { if (typeof done === 'function') { done(); } }); }); }; if (server.canProcessQueues()) { server.queueManager.processQueue('client_event_webhooks', queueProcessor); server.queueManager.processQueue('member_added_webhooks', queueProcessor); server.queueManager.processQueue('member_removed_webhooks', queueProcessor); server.queueManager.processQueue('channel_vacated_webhooks', queueProcessor); server.queueManager.processQueue('channel_occupied_webhooks', queueProcessor); server.queueManager.processQueue('cache_miss_webhooks', queueProcessor); } } sendClientEvent(app, channel, event, data, socketId, userId) { if (!app.hasClientEventWebhooks) { return; } let formattedData = { name: app_1.App.CLIENT_EVENT_WEBHOOK, channel, event, data, }; if (socketId) { formattedData.socket_id = socketId; } if (userId && utils_1.Utils.isPresenceChannel(channel)) { formattedData.user_id = userId; } this.send(app, formattedData, 'client_event_webhooks'); } sendMemberAdded(app, channel, userId) { if (!app.hasMemberAddedWebhooks) { return; } this.send(app, { name: app_1.App.MEMBER_ADDED_WEBHOOK, channel, user_id: userId, }, 'member_added_webhooks'); } sendMemberRemoved(app, channel, userId) { if (!app.hasMemberRemovedWebhooks) { return; } this.send(app, { name: app_1.App.MEMBER_REMOVED_WEBHOOK, channel, user_id: userId, }, 'member_removed_webhooks'); } sendChannelVacated(app, channel) { if (!app.hasChannelVacatedWebhooks) { return; } this.send(app, { name: app_1.App.CHANNEL_VACATED_WEBHOOK, channel, }, 'channel_vacated_webhooks'); } sendChannelOccupied(app, channel) { if (!app.hasChannelOccupiedWebhooks) { return; } this.send(app, { name: app_1.App.CHANNEL_OCCUPIED_WEBHOOK, channel, }, 'channel_occupied_webhooks'); } sendCacheMissed(app, channel) { if (!app.hasCacheMissedWebhooks) { return; } this.send(app, { name: app_1.App.CACHE_MISSED_WEBHOOK, channel, }, 'cache_miss_webhooks'); } send(app, data, queueName) { if (this.server.options.webhooks.batching.enabled) { this.sendWebhookByBatching(app, data, queueName); } else { this.sendWebhook(app, data, queueName); } } sendWebhook(app, data, queueName) { let events = data instanceof Array ? data : [data]; if (events.length === 0) { return; } let time = (new Date).getTime(); let payload = { time_ms: time, events, }; let originalPusherSignature = createWebhookHmac(JSON.stringify(payload), app.secret); this.server.queueManager.addToQueue(queueName, { appKey: app.key, appId: app.id, payload, originalPusherSignature, }); } sendWebhookByBatching(app, data, queueName) { this.batch.push(data); if (!this.batchHasLeader) { this.batchHasLeader = true; setTimeout(() => { if (this.batch.length > 0) { this.sendWebhook(app, this.batch.splice(0, this.batch.length), queueName); } this.batchHasLeader = false; }, this.server.options.webhooks.batching.duration); } } } exports.WebhookSender = WebhookSender;