From 59893f04583e2257aba30d61215d2e1fda4cc406 Mon Sep 17 00:00:00 2001 From: Dollerino Date: Thu, 19 Sep 2024 15:12:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-901=20(=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B8=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=BA=D0=BE=D0=BB=D0=BE=D0=B2=20MQTT=20=D0=B8=20KA?= =?UTF-8?q?FKA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.js | 30 ++ config_default.js | 30 ++ core/app.js | 12 +- core/in_queue.js | 421 +++++++++++++++- core/kafka_connector.js | 118 +++++ core/mqtt_connector.js | 89 ++++ core/out_queue.js | 72 +-- core/out_queue_processor.js | 121 ++++- core/utils.js | 56 +-- models/obj_service_function.js | 66 ++- package-lock.json | 844 +++++++++++++++++++++++++++++++++ package.json | 2 + 12 files changed, 1724 insertions(+), 137 deletions(-) create mode 100644 core/kafka_connector.js create mode 100644 core/mqtt_connector.js diff --git a/config.js b/config.js index 2ab47f0..fa73f2f 100644 --- a/config.js +++ b/config.js @@ -71,6 +71,34 @@ let inComing = { nPoolIncrement: 0 }; +//Параметры подключения к Kafka +let kafkaConnection = { + //ID клиента-отправителя + sClientIdSender: "Parus", + //ID клиента-получателя + sClientIdRecipient: "Parus", + //Время ожидания успешного подключения (мс) + nConnectionTimeout: 5000, + //Необходимость попытки переподключения при потере соединения + bRestartOnFailure: false, + //Время максимального ожидания между попытками переподключения (мс) + nMaxRetryTime: 20000, + //Время ожидания между попытками переподключения (мс) + nInitialRetryTime: 10000 +}; + +//Параметры подключения по MQTT протоколу +let mqttConnection = { + //ID клиента-отправителя + sClientIdSender: "Parus", + //ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться) + sClientIdRecipient: "ParusRecipient", + //Время ожидания успешного подключения (мс) + nConnectTimeout: 5000, + //Время ожидания между попытками переподключения (мс) + nReconnectPeriod: 10000 +}; + //Параметры отправки E-Mail уведомлений let mail = { //Адреc сервера SMTP @@ -98,5 +126,7 @@ module.exports = { dbConnect, outGoing, inComing, + kafkaConnection, + mqttConnection, mail }; diff --git a/config_default.js b/config_default.js index 4d2e174..1d5007f 100644 --- a/config_default.js +++ b/config_default.js @@ -71,6 +71,34 @@ let inComing = { nPoolIncrement: 0 }; +//Параметры подключения к Kafka +let kafkaConnection = { + //ID клиента-отправителя + sClientIdSender: "Parus", + //ID клиента-получателя + sClientIdRecipient: "Parus", + //Время ожидания успешного подключения (мс) + nConnectionTimeout: 5000, + //Необходимость попытки переподключения при потере соединения + bRestartOnFailure: false, + //Время максимального ожидания между попытками переподключения (мс) + nMaxRetryTime: 20000, + //Время ожидания между попытками переподключения (мс) + nInitialRetryTime: 10000 +}; + +//Параметры подключения по MQTT протоколу +let mqttConnection = { + //ID клиента-отправителя + sClientIdSender: "Parus", + //ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться) + sClientIdRecipient: "ParusRecipient", + //Время ожидания успешного подключения (мс) + nConnectTimeout: 5000, + //Время ожидания между попытками переподключения (мс) + nReconnectPeriod: 10000 +}; + //Параметры отправки E-Mail уведомлений let mail = { //Адреc сервера SMTP @@ -98,5 +126,7 @@ module.exports = { dbConnect, outGoing, inComing, + kafkaConnection, + mqttConnection, mail }; diff --git a/core/app.js b/core/app.js index 77f3e4e..cf406d5 100644 --- a/core/app.js +++ b/core/app.js @@ -196,9 +196,7 @@ class ParusAppServer { //Если настройки верны - будем стартовать if (!sCheckResult) { //Протоколируем версию и релиз - await this.logger.info( - `Версия сервера приложений: ${prms.config.common.sVersion}, релиз: ${prms.config.common.sRelease}` - ); + await this.logger.info(`Версия сервера приложений: ${prms.config.common.sVersion}, релиз: ${prms.config.common.sRelease}`); //Создаём подключение к БД this.dbConn = new db.DBConnector({ connectSettings: { @@ -219,7 +217,9 @@ class ParusAppServer { dbConn: this.dbConn, logger: this.logger, notifier: this.notifier, - sProxy: prms.config.outGoing.sProxy + sProxy: prms.config.outGoing.sProxy, + kafkaConnectionPrms: prms.config.kafkaConnection, + mqttConnectionPrms: prms.config.mqttConnection }); //Создаём обработчик очереди входящих this.inQ = new iq.InQueue({ @@ -227,7 +227,9 @@ class ParusAppServer { inComing: prms.config.inComing, dbConn: this.dbConn, logger: this.logger, - notifier: this.notifier + notifier: this.notifier, + kafkaConnectionPrms: prms.config.kafkaConnection, + mqttConnectionPrms: prms.config.mqttConnection }); //Создаём контроллер доступности удалённых сервисов this.srvAvlCtrl = new sac.ServiceAvailableController({ diff --git a/core/in_queue.js b/core/in_queue.js index cb2f519..1237e04 100644 --- a/core/in_queue.js +++ b/core/in_queue.js @@ -13,7 +13,16 @@ const express = require("express"); //WEB-сервер Express const cors = require("cors"); //Управление заголовками безопасности для WEB-сервера Express const bodyParser = require("body-parser"); //Модуль для Express (разбор тела входящего запроса) const { ServerError } = require("./server_errors"); //Типовая ошибка -const { makeErrorText, validateObject, buildURL, getAppSrvFunction, buildOptionsXML, parseOptionsXML, deepMerge } = require("./utils"); //Вспомогательные функции +const { + makeErrorText, + validateObject, + buildURL, + getAppSrvFunction, + buildOptionsXML, + parseOptionsXML, + deepMerge, + getKafkaBroker +} = require("./utils"); //Вспомогательные функции const { NINC_EXEC_CNT_YES, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД const objInQueueSchema = require("../models/obj_in_queue"); //Схема валидации сообщений обмена с бработчиком очереди входящих сообщений const objServiceSchema = require("../models/obj_service"); //Схемы валидации сервиса @@ -28,6 +37,8 @@ const { SERR_DB_SERVER, SERR_UNAUTH } = require("./constants"); //Общесистемные константы +const { subscribeMQTT } = require("./mqtt_connector"); //Модуль для работы с MQTT +const { subscribeKafka } = require("./kafka_connector"); //Модуль для работы с Kafka //-------------------------- // Глобальные идентификаторы @@ -71,6 +82,13 @@ class InQueue extends EventEmitter { this.webApp.options("*", cors()); //WEB-сервер this.srv = null; + //Параметры подключения к Kafka + this.kafkaConnectionPrms = _.cloneDeep(prms.kafkaConnectionPrms); + //Параметры подключения к MQTT + this.mqttConnectionPrms = _.cloneDeep(prms.mqttConnectionPrms); + //Внешние подключения + this.kafkaConnections = []; + this.mqttConnections = []; } else { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } @@ -360,6 +378,326 @@ class InQueue extends EventEmitter { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } } + + //Обработка сообщения kafka + async processKafkaMessage({ message, service, fn }) { + //Буфер для сообщения очереди + let q = null; + try { + //Тело сообщения и ответ на него + let blMsg = null; + let blResp = null; + //Параметры сообщения и ответа на него + let options = {}; + let optionsResp = {}; + //Флаг прекращения обработки сообщения + let bStopPropagation = false; + //Получим тело сообщения + blMsg = message.value ? message.value : null; + //Определимся с параметрами сообщения полученными от внешней системы + options = { + method: fn.sFnPrmsType, + headers: _.cloneDeep(message.headers) + }; + //Кладём сообщение в очередь + q = await this.dbConn.putQueue({ + nServiceFnId: fn.nId, + sOptions: buildOptionsXML({ options }), + blMsg + }); + //Скажем что пришло новое входящее сообщение + await this.logger.info( + `Новое входящее Kafka-сообщение для функции ${fn.sCode} (${buildURL({ + sSrvRoot: service.sSrvRoot, + sFnURL: fn.sFnURL + })})`, + { nQueueId: q.nId } + ); + //Вызываем обработчик со стороны БД (если он есть) + if (fn.sPrcResp) { + //Фиксируем начало исполнения сервером БД - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB + }); + //Вызов обработчика БД + let prcRes = await this.dbConn.execQueueDBPrc({ nQueueId: q.nId }); + //Если результат - ошибка пробрасываем её + if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_ERR) throw new ServerError(SERR_DB_SERVER, prcRes.sMsg); + //Если результат - ошибка аутентификации, то и её пробрасываем, но с правильным кодом + if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_UNAUTH) throw new ServerError(SERR_UNAUTH, prcRes.sMsg || "Нет аутентификации"); + //Выставим статус сообщению очереди - исполнено обработчиком БД + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB_OK + }); + //Считаем ответ полученный от системы + let qData = await this.dbConn.getQueueResp({ nQueueId: q.nId }); + blResp = qData.blResp; + } + //Выполняем обработчик "После" (если он есть) + if (bStopPropagation === false && fn.sAppSrvAfter) { + //Выставим статус сообщению очереди - исполняется сервером приложений + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP + }); + //Выполняем + const fnAfter = getAppSrvFunction(fn.sAppSrvAfter); + let resAfter = null; + try { + let resAfterPrms = { res: { body: blMsg }, service: service, function: fn }; + resAfterPrms.queue = _.cloneDeep(q); + resAfterPrms.queue.blMsg = blMsg; + resAfterPrms.queue.blResp = blResp; + resAfterPrms.options = _.cloneDeep(options); + resAfterPrms.optionsResp = _.cloneDeep(optionsResp); + resAfterPrms.dbConn = this.dbConn; + resAfterPrms.notifier = this.notifier; + resAfter = await fnAfter(resAfterPrms); + } catch (e) { + throw new ServerError(SERR_APP_SERVER_AFTER, e.message); + } + //Проверяем структуру ответа функции предобработки + if (resAfter) { + let sCheckResult = validateObject( + resAfter, + objInQueueSchema.InQueueProcessorFnAfter, + "Результат функции постобработки входящего сообщения" + ); + //Если структура ответа в норме + if (!sCheckResult) { + //Выставим статус сообщению очереди - исполнено сервером приложений + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP_OK + }); + //Фиксируем результат исполнения "После" - ответ системы + if (!_.isUndefined(resAfter.blResp)) { + blResp = resAfter.blResp; + q = await this.dbConn.setQueueResp({ + nQueueId: q.nId, + blResp, + nIsOriginal: NIS_ORIGINAL_NO + }); + } + //Фиксируем результат исполнения "После" - параметры ответа на запрос + if (!_.isUndefined(resAfter.optionsResp)) { + optionsResp = deepMerge(optionsResp, resAfter.optionsResp); + let sOptionsResp = buildOptionsXML({ options: optionsResp }); + q = await this.dbConn.setQueueOptionsResp({ nQueueId: q.nId, sOptionsResp }); + } + //Если пришел флаг ошибочной аутентификации и он положительный - то это ошибка, дальше ничего не делаем + if (!_.isUndefined(resAfter.bUnAuth)) if (resAfter.bUnAuth === true) throw new ServerError(SERR_UNAUTH, "Нет аутентификации"); + } else { + //Или расскажем об ошибке + throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); + } + } + } + //Фиксируем успех обработки - в протоколе работы сервиса + await this.logger.info(`Входящее сообщение ${q.nId} успешно отработано`, { nQueueId: q.nId }); + //Фиксируем успех обработки - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nIncExecCnt: NINC_EXEC_CNT_YES, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_OK + }); + } catch (e) { + //Тема и текст уведомления об ошибке + let sSubject = `Ошибка обработки входящего сообщения сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`; + let sMessage = makeErrorText(e); + //Если сообщение очереди успели создать + if (q) { + //Фиксируем ошибку обработки сервером приложений - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + sExecMsg: sMessage, + nIncExecCnt: NINC_EXEC_CNT_YES, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_ERR + }); + //Фиксируем ошибку обработки сервером приложений - в протоколе работы сервиса + await this.logger.error(`Ошибка обработки входящего сообщения ${q.nId} сервером приложений: ${sMessage}`, { nQueueId: q.nId }); + //Добавим чуть больше информации в тему сообщения + sSubject = `Ошибка обработки входящего сообщения ${q.nId} сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`; + } else { + //Ограничимся общей ошибкой + await this.logger.error(sMessage, { + nServiceId: service.nId, + nServiceFnId: fn.nId + }); + } + //Если для функции-обработчика указан признак необходимости оповещения об ошибках + if (fn.nErrNtfSign == objServiceFnSchema.NERR_NTF_SIGN_YES) { + //Отправим уведомление об ошибке отработки в почту + await this.notifier.addMessage({ + sTo: fn.sErrNtfMail, + sSubject, + sMessage + }); + } + } + } + + //Обработка сообщения + async processMQTTMessage({ message, service, fn }) { + //Буфер для сообщения очереди + let q = null; + try { + //Тело сообщения и ответ на него + let blMsg = null; + let blResp = null; + //Параметры сообщения и ответа на него + let options = {}; + let optionsResp = {}; + //Получим тело сообщения + blMsg = message ? message : null; + //Определимся с параметрами сообщения полученными от внешней системы + options = { + method: fn.sFnPrmsType + }; + console.log("Кладём в очередь"); + //Кладём сообщение в очередь + q = await this.dbConn.putQueue({ + nServiceFnId: fn.nId, + sOptions: buildOptionsXML({ options }), + blMsg + }); + console.log("Продолжаем"); + //Скажем что пришло новое входящее сообщение + await this.logger.info( + `Новое входящее MQTT-сообщение для функции ${fn.sCode} (${buildURL({ + sSrvRoot: service.sSrvRoot, + sFnURL: fn.sFnURL + })})`, + { nQueueId: q.nId } + ); + //Вызываем обработчик со стороны БД (если он есть) + if (fn.sPrcResp) { + //Фиксируем начало исполнения сервером БД - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB + }); + //Вызов обработчика БД + let prcRes = await this.dbConn.execQueueDBPrc({ nQueueId: q.nId }); + //Если результат - ошибка пробрасываем её + if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_ERR) throw new ServerError(SERR_DB_SERVER, prcRes.sMsg); + //Если результат - ошибка аутентификации, то и её пробрасываем, но с правильным кодом + if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_UNAUTH) throw new ServerError(SERR_UNAUTH, prcRes.sMsg || "Нет аутентификации"); + //Выставим статус сообщению очереди - исполнено обработчиком БД + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB_OK + }); + //Считаем ответ полученный от системы + let qData = await this.dbConn.getQueueResp({ nQueueId: q.nId }); + blResp = qData.blResp; + } + //Выполняем обработчик "После" (если он есть) + if (fn.sAppSrvAfter) { + //Выставим статус сообщению очереди - исполняется сервером приложений + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP + }); + //Выполняем + const fnAfter = getAppSrvFunction(fn.sAppSrvAfter); + let resAfter = null; + try { + let resAfterPrms = { res: { body: message }, service: service, function: fn }; + resAfterPrms.queue = _.cloneDeep(q); + resAfterPrms.queue.blMsg = blMsg; + resAfterPrms.queue.blResp = blResp; + resAfterPrms.options = _.cloneDeep(options); + resAfterPrms.optionsResp = _.cloneDeep(optionsResp); + resAfterPrms.dbConn = this.dbConn; + resAfterPrms.notifier = this.notifier; + resAfter = await fnAfter(resAfterPrms); + } catch (e) { + throw new ServerError(SERR_APP_SERVER_AFTER, e.message); + } + //Проверяем структуру ответа функции предобработки + if (resAfter) { + let sCheckResult = validateObject( + resAfter, + objInQueueSchema.InQueueProcessorFnAfter, + "Результат функции постобработки входящего сообщения" + ); + //Если структура ответа в норме + if (!sCheckResult) { + //Выставим статус сообщению очереди - исполнено сервером приложений + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP_OK + }); + //Фиксируем результат исполнения "После" - ответ системы + if (!_.isUndefined(resAfter.blResp)) { + blResp = resAfter.blResp; + q = await this.dbConn.setQueueResp({ + nQueueId: q.nId, + blResp, + nIsOriginal: NIS_ORIGINAL_NO + }); + } + //Фиксируем результат исполнения "После" - параметры ответа на запрос + if (!_.isUndefined(resAfter.optionsResp)) { + optionsResp = deepMerge(optionsResp, resAfter.optionsResp); + let sOptionsResp = buildOptionsXML({ options: optionsResp }); + q = await this.dbConn.setQueueOptionsResp({ nQueueId: q.nId, sOptionsResp }); + } + //Если пришел флаг ошибочной аутентификации и он положительный - то это ошибка, дальше ничего не делаем + if (!_.isUndefined(resAfter.bUnAuth)) if (resAfter.bUnAuth === true) throw new ServerError(SERR_UNAUTH, "Нет аутентификации"); + } else { + //Или расскажем об ошибке + throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); + } + } + } + //Фиксируем успех обработки - в протоколе работы сервиса + await this.logger.info(`Входящее сообщение ${q.nId} успешно отработано`, { nQueueId: q.nId }); + //Фиксируем успех обработки - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + nIncExecCnt: NINC_EXEC_CNT_YES, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_OK + }); + } catch (e) { + //Тема и текст уведомления об ошибке + let sSubject = `Ошибка обработки входящего сообщения сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`; + let sMessage = makeErrorText(e); + //Если сообщение очереди успели создать + if (q) { + //Фиксируем ошибку обработки сервером приложений - в статусе сообщения + q = await this.dbConn.setQueueState({ + nQueueId: q.nId, + sExecMsg: sMessage, + nIncExecCnt: NINC_EXEC_CNT_YES, + nExecState: objQueueSchema.NQUEUE_EXEC_STATE_ERR + }); + //Фиксируем ошибку обработки сервером приложений - в протоколе работы сервиса + await this.logger.error(`Ошибка обработки входящего сообщения ${q.nId} сервером приложений: ${sMessage}`, { nQueueId: q.nId }); + //Добавим чуть больше информации в тему сообщения + sSubject = `Ошибка обработки входящего сообщения ${q.nId} сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`; + } else { + //Ограничимся общей ошибкой + await this.logger.error(sMessage, { + nServiceId: service.nId, + nServiceFnId: fn.nId + }); + } + //Если для функции-обработчика указан признак необходимости оповещения об ошибках + if (fn.nErrNtfSign == objServiceFnSchema.NERR_NTF_SIGN_YES) { + //Отправим уведомление об ошибке отработки в почту + await this.notifier.addMessage({ + sTo: fn.sErrNtfMail, + sSubject, + sMessage + }); + } + } + } + //Запуск обработки очереди входящих сообщений startProcessing(prms) { //Проверяем структуру переданного объекта для старта @@ -428,6 +766,58 @@ class InQueue extends EventEmitter { } ); }); + //Считываем прием сообщений по Kafka + let kafkaSrvs = _.filter(this.services, srv => { + return srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE && srv.sSrvRoot.startsWith("kafka://"); + }); + //Если есть сервисы с приемом сообщений по Kafka + if (kafkaSrvs.length !== 0) { + //Обходим данные сервисы + _.forEach(kafkaSrvs, async srvs => { + //Если у сервиса обмена есть функции + if (srvs.functions.length !== 0) { + //Подключаемся и подписываемся на соответствующий брокер + let connectionKafka = await subscribeKafka({ + connectionPrms: this.kafkaConnectionPrms, + service: srvs, + processKafkaMessage: prms => this.processKafkaMessage(prms), + logger: this.logger + }); + //Если подключение было создано + if (connectionKafka) { + //Добавляем в общий список подключений kafka + this.kafkaConnections.push(connectionKafka); + } + } + }); + } + //Считываем прием сообщений по MQTT + let mqttSrvs = _.filter(this.services, srv => { + return ( + srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE && (srv.sSrvRoot.startsWith("mqtt://") || srv.sSrvRoot.startsWith("mqtts://")) + ); + }); + //Если есть сервисы с приемом сообщений по MQTT + if (mqttSrvs.length !== 0) { + //Обходим данные сервисы + _.forEach(mqttSrvs, async srvs => { + //Если у сервиса обмена есть функции + if (srvs.functions.length !== 0) { + //Подключаемся и подписываемся на соответствующий брокер + let connectionMQTT = await subscribeMQTT({ + connectionPrms: this.mqttConnectionPrms, + service: srvs, + processMQTTMessage: prms => this.processMQTTMessage(prms), + logger: this.logger + }); + //Если подключение было создано + if (connectionMQTT) { + //Добавляем в общий список подключений kafka + this.mqttConnections.push(connectionMQTT); + } + } + }); + } //Запросы на адреса, не входящие в состав объявленных сервисов - 404 NOT FOUND this.webApp.use("*", (req, res) => { res.status(404).send( @@ -453,10 +843,39 @@ class InQueue extends EventEmitter { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } } + + //Закрытие подключений, если они есть + stopConnections() { + //Если у нас есть соединения с MQTT + if (this.mqttConnections.length !== 0) { + //Закрываем их + _.forEach(this.mqttConnections, async connection => { + try { + await connection.end(); + } catch (e) { + await this.logger.error(`Ошибка завершения MQTT подключения: ${makeErrorText(e)}`); + } + }); + } + //Если у нас есть соединения с Kafka + if (this.kafkaConnections.length !== 0) { + //Закрываем их + _.forEach(this.kafkaConnections, async connection => { + try { + await connection.disconnect(); + } catch (e) { + await this.logger.error(`Ошибка завершения Kafka подключения: ${makeErrorText(e)}`); + } + }); + } + } + //Остановка обработки очереди исходящих сообщений stopProcessing() { //Выставляем флаг неработы this.bWorking = false; + //Закрываем подключения, если они есть + this.stopConnections(); //Останавливаем WEB-сервер (если создавался) if (this.srv) { this.srv.close(() => { diff --git a/core/kafka_connector.js b/core/kafka_connector.js new file mode 100644 index 0000000..5ee2841 --- /dev/null +++ b/core/kafka_connector.js @@ -0,0 +1,118 @@ +/* + Сервис интеграции ПП Парус 8 с WEB API + Модуль ядра: обработчик kafka сообщений +*/ + +//---------------------- +// Подключение библиотек +//---------------------- + +const _ = require("lodash"); //Работа с массивами и коллекциями +const { makeErrorText, getKafkaBroker, getKafkaAuth } = require("./utils"); //Вспомогательные функции +const { Kafka, logLevel } = require("kafkajs"); //Работа с Kafka + +//------------ +// Тело модуля +//------------ + +//Отправка сообщения Kafka +const publishKafka = async ({ connectionPrms, url, auth, topic, message }) => { + //Иницализируем подключение к Kafka + let kafka = new Kafka({ + clientId: connectionPrms.sClientIdSender, + brokers: [url], + connectionTimeout: connectionPrms.nConnectionTimeout, + logLevel: logLevel.NOTHING, + ...auth + }); + //Инициализируем продюсера + let producer = kafka.producer(); + //Подключаемся к Kafka + await producer.connect(); + //Отправляем сообщение + let res = await producer.send({ topic: topic, messages: [{ value: message }] }); + //Отключаемся + await producer.disconnect(); + //Возвращаем ответ + return res; +}; + +//Получение MQTT сообщений +const subscribeKafka = async ({ connectionPrms, service, processKafkaMessage, logger }) => { + try { + //Признак необходимости вывода сообщения о потере соединения + let bLogLostConnection = true; + //Получаем брокера по URL сервиса + let sBroker = getKafkaBroker(service.sSrvRoot); + //Иницализируем подключение к Kafka + let client = new Kafka({ + clientId: connectionPrms.sClientIdRecipient, + brokers: [sBroker], + connectionTimeout: connectionPrms.nConnectionTimeout, + ...getKafkaAuth(service.sSrvUser, service.sSrvPass), + logLevel: logLevel.NOTHING, + retry: { + retries: 0, + maxRetryTime: connectionPrms.nMaxRetryTime, + initialRetryTime: connectionPrms.nInitialRetryTime, + restartOnFailure: error => { + return new Promise(resolve => { + //Если требуется вывести ошибку + if (bLogLostConnection) { + //Выводим ошибку + logger.error(`Соединение с Kafka потеряно (${sBroker}): ${makeErrorText(error)}`); + //Сбрасываем признак необходимости вывода ошибки + bLogLostConnection = false; + } + resolve(connectionPrms.bRestartOnFailure); + }); + } + } + }); + + //Инициализируем получателя + let consumer = client.consumer({ groupId: "ParusWebApi" }); + + //Устанавливаем прослушивание + await consumer.connect(); + consumer.subscribe({ topics: _.map(service.functions, "sFnURL") }); + + //Запускаем прослушивание необходимых топиков + consumer.run({ + eachMessage: async ({ topic, message }) => { + try { + //Вызываем обработчик + processKafkaMessage({ + message, + service, + fn: _.find(service.functions, { sFnURL: topic }) + }); + } catch (e) { + await logger.error(`Ошибка обработки исходящего сообщения Kafka: ${makeErrorText(e)}`); + } + } + }); + + //Отслеживаем соединение + consumer.on(consumer.events.CONNECT, () => { + //Если сообщение о потере соединения уже выводилось + if (!bLogLostConnection) { + //Сообщим о восстановлении соединения + logger.info(`Соединение с Kafka восстановлено (${sBroker})`); + //Устанавливаем признак сообщения о потере соединения + bLogLostConnection = true; + } + }); + //Возвращаем соединение + return consumer; + } catch (e) { + await logger.error(`Ошибка запуска обработчика очереди исходящих сообщений Kafka: ${makeErrorText(e)}`); + } +}; + +//----------------- +// Интерфейс модуля +//----------------- + +exports.publishKafka = publishKafka; +exports.subscribeKafka = subscribeKafka; diff --git a/core/mqtt_connector.js b/core/mqtt_connector.js new file mode 100644 index 0000000..48fcbc6 --- /dev/null +++ b/core/mqtt_connector.js @@ -0,0 +1,89 @@ +/* + Сервис интеграции ПП Парус 8 с WEB API + Модуль ядра: обработчик mqtt сообщений +*/ + +//---------------------- +// Подключение библиотек +//---------------------- + +const _ = require("lodash"); //Работа с массивами и коллекциями +const { makeErrorText } = require("./utils"); //Вспомогательные функции +const mqtt = require("mqtt"); //Работа с MQTT + +//------------ +// Тело модуля +//------------ + +//Отправка MQTT сообщения +const publishMQTT = async ({ connectionPrms, url, auth, topic, message }) => { + //Инициализируем подключение + const client = await mqtt.connectAsync(url, { + clientId: connectionPrms.sClientIdSender, + clean: true, + connectTimeout: connectionPrms.nConnectTimeout, + username: auth.user, + password: auth.pass, + reconnectPeriod: connectionPrms.nReconnectPeriod + }); + //Отправляем сообщение + await client.publishAsync(topic, message); + //Закрываем подключение + await client.endAsync(); + //Возвращаем сообщение, которое было отправлено + return { statusCode: 200 }; +}; + +//Получение MQTT сообщений +const subscribeMQTT = async ({ connectionPrms, service, processMQTTMessage, logger }) => { + try { + //Инициализируем строку подключения + let sBroker = service.sSrvRoot; + //Инициализируем подключение + const client = await mqtt.connectAsync(sBroker, { + clientId: connectionPrms.sClientIdRecipient, + clean: true, + connectTimeout: connectionPrms.nConnectTimeout, + username: service.sSrvUser, + password: service.sSrvPass, + reconnectPeriod: connectionPrms.nReconnectPeriod + }); + + //Обходим функции сервиса + _.forEach( + _.filter(service.functions, fn => !fn.sFnURL.startsWith("@")), + fn => { + client.subscribe(fn.sFnURL); + } + ); + + //Прослушиваем сообщения + client.on("message", (topic, message) => { + console.log("message"); + //Обрабатываем сообщение + processMQTTMessage({ message, service, fn: _.find(service.functions, { sFnURL: topic }) }); + }); + //Прослушиваем отключение от сервера + client.on("offline", () => { + //Выводим ошибку + logger.error(`Соединение с MQTT потеряно (${sBroker})`); + }); + //Прослушиваем восстановление соединения + client.on("reconnect", () => { + //Сообщим о восстановлении соединения + logger.info(`Соединение с MQTT восстановлено (${sBroker})`); + }); + + //Возвращаем подключение + return client; + } catch (e) { + logger.error(`Ошибка запуска обработчика очереди исходящих сообщений MQTT: ${makeErrorText(e)}`); + } +}; + +//----------------- +// Интерфейс модуля +//----------------- + +exports.publishMQTT = publishMQTT; +exports.subscribeMQTT = subscribeMQTT; diff --git a/core/out_queue.js b/core/out_queue.js index 1c60d8b..c15acf7 100644 --- a/core/out_queue.js +++ b/core/out_queue.js @@ -70,6 +70,10 @@ class OutQueue extends EventEmitter { this.inProgress = []; //Привяжем методы к указателю на себя для использования в обработчиках событий this.outDetectingLoop = this.outDetectingLoop.bind(this); + //Параметры подключения к Kafka + this.kafkaConnectionPrms = _.cloneDeep(prms.kafkaConnectionPrms); + //Параметры подключения к MQTT + this.mqttConnectionPrms = _.cloneDeep(prms.mqttConnectionPrms); } else { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } @@ -141,11 +145,7 @@ class OutQueue extends EventEmitter { //Старт обработчика startQueueProcessor(prms) { //Проверяем структуру переданного объекта для старта обработчика - let sCheckResult = validateObject( - prms, - prmsOutQueueSchema.startQueueProcessor, - "Параметры функции запуска обработчика сообщения очереди" - ); + let sCheckResult = validateObject(prms, prmsOutQueueSchema.startQueueProcessor, "Параметры функции запуска обработчика сообщения очереди"); //Если структура объекта в норме if (!sCheckResult) { //Добавляем идентификатор позиции очереди в список обрабатываемых @@ -163,7 +163,9 @@ class OutQueue extends EventEmitter { function: _.find(_.find(this.services, { nId: prms.queue.nServiceId }).functions, { nId: prms.queue.nServiceFnId }), - sProxy: this.sProxy + sProxy: this.sProxy, + kafkaConnectionPrms: this.kafkaConnectionPrms, + mqttConnectionPrms: this.mqttConnectionPrms }); //Уменьшаем количество доступных обработчиков this.nWorkersLeft--; @@ -174,11 +176,7 @@ class OutQueue extends EventEmitter { //Останов обработчика stopQueueProcessor(prms) { //Проверяем структуру переданного объекта для останова обработчика - let sCheckResult = validateObject( - prms, - prmsOutQueueSchema.stopQueueProcessor, - "Параметры функции останова обработчика сообщения очереди" - ); + let sCheckResult = validateObject(prms, prmsOutQueueSchema.stopQueueProcessor, "Параметры функции останова обработчика сообщения очереди"); //Если структура объекта в норме if (!sCheckResult) { //Удаляем идентификатор позиции очереди из списка обрабатываемых @@ -219,19 +217,13 @@ class OutQueue extends EventEmitter { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } } catch (e) { - await this.logger.error( - `При отправке уведомления об ошибке обработки исходящего сообщения: ${makeErrorText(e)}` - ); + await this.logger.error(`При отправке уведомления об ошибке обработки исходящего сообщения: ${makeErrorText(e)}`); } } //Запуск обработки очередного сообщения processMessage(prms) { //Проверяем структуру переданного объекта - let sCheckResult = validateObject( - prms, - prmsOutQueueSchema.processMessage, - "Параметры функции запуска обработки очередного сообщения" - ); + let sCheckResult = validateObject(prms, prmsOutQueueSchema.processMessage, "Параметры функции запуска обработки очередного сообщения"); //Если структура объекта в норме if (!sCheckResult) { //Проверим, что есть доступные обработчики @@ -276,10 +268,9 @@ class OutQueue extends EventEmitter { }); } catch (e) { //Отразим в протоколе ошибку постановки задачи на аутентификацию сервиса - await self.logger.error( - `Ошибка постановки задачи на аутентификацию сервиса: ${makeErrorText(e)}`, - { nQueueId: prms.queue.nId } - ); + await self.logger.error(`Ошибка постановки задачи на аутентификацию сервиса: ${makeErrorText(e)}`, { + nQueueId: prms.queue.nId + }); } } } @@ -298,25 +289,21 @@ class OutQueue extends EventEmitter { sExecMsg: sError, nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO, nExecState: - (nQueueOldExecCnt == prms.queue.nExecCnt - ? prms.queue.nExecCnt + 1 - : prms.queue.nExecCnt) < prms.queue.nRetryAttempts + (nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < prms.queue.nRetryAttempts ? prms.queue.nExecState : objQueueSchema.NQUEUE_EXEC_STATE_ERR }); } //Если исполнение завершилось полностью и с ошибкой - расскажем об этом - if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) - await this.notifyMessageProcessError(prms); + if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError(prms); //Останавливаем обработчик и инкрементируем флаг их доступного количества try { this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc }); } catch (e) { //Отразим в протоколе ошибку останова - await self.logger.error( - `Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, - { nQueueId: prms.queue.nId } - ); + await self.logger.error(`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, { + nQueueId: prms.queue.nId + }); } }); //Перехват ошибок обработчика @@ -333,23 +320,20 @@ class OutQueue extends EventEmitter { sExecMsg: makeErrorText(e), nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO, nExecState: - (nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < - prms.queue.nRetryAttempts + (nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < prms.queue.nRetryAttempts ? prms.queue.nExecState : objQueueSchema.NQUEUE_EXEC_STATE_ERR }); //Если исполнение завершилось полностью и с ошибкой - расскажем об этом - if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) - await this.notifyMessageProcessError(prms); + if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError(prms); //Останавливаем обработчик и инкрементируем флаг их доступного количества try { this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc }); } catch (e) { //Отразим в протоколе ошибку останова - await self.logger.error( - `Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, - { nQueueId: prms.queue.nId } - ); + await self.logger.error(`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, { + nQueueId: prms.queue.nId + }); } }); //Перехват останова обработчика @@ -392,16 +376,12 @@ class OutQueue extends EventEmitter { nQueueId: outMsg.nId, sExecMsg: makeErrorText(e), nIncExecCnt: NINC_EXEC_CNT_YES, - nExecState: - outMsg.nExecCnt + 1 < outMsg.nRetryAttempts - ? outMsg.nExecState - : objQueueSchema.NQUEUE_EXEC_STATE_ERR + nExecState: outMsg.nExecCnt + 1 < outMsg.nRetryAttempts ? outMsg.nExecState : objQueueSchema.NQUEUE_EXEC_STATE_ERR }); //Фиксируем ошибку обработки сервером приложений - запись в протокол работы сервера приложений await this.logger.error(makeErrorText(e), { nQueueId: outMsg.nId }); //Если исполнение завершилось полностью и с ошибкой - расскажем об этом - if (queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) - await this.notifyMessageProcessError({ queue }); + if (queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError({ queue }); } } } diff --git a/core/out_queue_processor.js b/core/out_queue_processor.js index 05c09dc..867ac5f 100644 --- a/core/out_queue_processor.js +++ b/core/out_queue_processor.js @@ -12,7 +12,18 @@ const _ = require("lodash"); //Работа с массивами и объек const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами const lg = require("./logger"); //Протоколирование работы const db = require("./db_connector"); //Взаимодействие с БД -const { makeErrorText, validateObject, getAppSrvFunction, buildURL, parseOptionsXML, buildOptionsXML, deepMerge } = require("./utils"); //Вспомогательные функции +const { + makeErrorText, + validateObject, + getAppSrvFunction, + buildURL, + parseOptionsXML, + buildOptionsXML, + deepMerge, + getKafkaBroker, + getKafkaAuth, + getURLProtocol +} = require("./utils"); //Вспомогательные функции const { ServerError } = require("./server_errors"); //Типовая ошибка const objOutQueueProcessorSchema = require("../models/obj_out_queue_processor"); //Схема валидации сообщений обмена с бработчиком очереди исходящих сообщений const prmsOutQueueProcessorSchema = require("../models/prms_out_queue_processor"); //Схема валидации параметров функций модуля @@ -28,6 +39,8 @@ const { SERR_UNAUTH } = require("./constants"); //Глобальные константы const { NINC_EXEC_CNT_YES, NINC_EXEC_CNT_NO, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД +const { publishMQTT } = require("./mqtt_connector"); //Работа с MQTT/MQTTS запросами +const { publishKafka } = require("./kafka_connector"); //Работа с Kafka запросами //-------------------------- // Глобальные идентификаторы @@ -129,21 +142,53 @@ const appProcess = async prms => { let optionsResp = {}; //Флаг прекращения обработки сообщения let bStopPropagation = false; - //Определимся с URL и телом сообщения в зависимости от способа передачи параметров (для POST, PATCH и PUT - данные в теле, для остальных - в URI) - if ( - [objServiceFnSchema.NFN_PRMS_TYPE_POST, objServiceFnSchema.NFN_PRMS_TYPE_PATCH, objServiceFnSchema.NFN_PRMS_TYPE_PUT].includes( - prms.function.nFnPrmsType - ) - ) { - options.url = buildURL({ sSrvRoot: prms.service.sSrvRoot, sFnURL: prms.function.sFnURL }); - options.body = prms.queue.blMsg; - options.headers = { "content-type": "application/octet-stream" }; - } else { - options.url = buildURL({ - sSrvRoot: prms.service.sSrvRoot, - sFnURL: prms.function.sFnURL, - sQuery: prms.queue.blMsg === null ? "" : prms.queue.blMsg.toString() - }); + //Флаг выполнения обработчика "До" + let bExecuteBefore = true; + //Флаг выполнения обработчика "После" + let bExecuteAfter = true; + //Считываем протокол работы + let sProtocol = getURLProtocol(prms.service.sSrvRoot); + //Исходя из протокола собираем параметры + switch (true) { + //Kafka + case sProtocol === objServiceFnSchema.SPROTOCOL_KAFKA: + options.url = getKafkaBroker(prms.service.sSrvRoot); + options.body = prms.queue.blMsg; + options.topic = prms.function.sFnURL; + options.auth = getKafkaAuth(prms.service.sSrvUser, prms.service.sSrvPass); + //Указываем, что выполнение обработчика "После" невозможно + bExecuteAfter = false; + break; + //mqtt и mqtts + case [objServiceFnSchema.SPROTOCOL_MQTT, objServiceFnSchema.SPROTOCOL_MQTTS].includes(sProtocol): + options.url = prms.service.sSrvRoot; + options.body = prms.queue.blMsg; + options.topic = prms.function.sFnURL; + options.auth = { user: prms.service.sSrvUser, pass: prms.service.sSrvPass }; + //Указываем, что выполнение обработчика "После" невозможно + bExecuteAfter = false; + break; + //Другие + default: + //Определимся с URL и телом сообщения в зависимости от способа передачи параметров (для POST, PATCH и PUT - данные в теле, для остальных - в URI) + if ( + [ + objServiceFnSchema.NFN_PRMS_TYPE_POST, + objServiceFnSchema.NFN_PRMS_TYPE_PATCH, + objServiceFnSchema.NFN_PRMS_TYPE_PUT + ].includes(prms.function.nFnPrmsType) + ) { + options.url = buildURL({ sSrvRoot: prms.service.sSrvRoot, sFnURL: prms.function.sFnURL }); + options.body = prms.queue.blMsg; + options.headers = { "content-type": "application/octet-stream" }; + } else { + options.url = buildURL({ + sSrvRoot: prms.service.sSrvRoot, + sFnURL: prms.function.sFnURL, + sQuery: prms.queue.blMsg === null ? "" : prms.queue.blMsg.toString() + }); + } + break; } // Если у сервиса указан прокси, либо у приложения установлен глобальный прокси if (prms.service.sProxyURL || prms.sProxy) { @@ -167,7 +212,7 @@ const appProcess = async prms => { } } //Выполняем обработчик "До" (если он есть) - if (prms.function.sAppSrvBefore) { + if (prms.function.sAppSrvBefore && bExecuteBefore) { const fnBefore = getAppSrvFunction(prms.function.sAppSrvBefore); let resBefore = null; try { @@ -199,7 +244,12 @@ const appProcess = async prms => { objServiceFnSchema.NFN_PRMS_TYPE_POST, objServiceFnSchema.NFN_PRMS_TYPE_PATCH, objServiceFnSchema.NFN_PRMS_TYPE_PUT - ].includes(prms.function.nFnPrmsType) + ].includes(prms.function.nFnPrmsType) || + [ + objServiceFnSchema.SPROTOCOL_KAFKA, + objServiceFnSchema.SPROTOCOL_MQTT, + objServiceFnSchema.SPROTOCOL_MQTTS + ].includes(sProtocol) ) { options.body = prms.queue.blMsg; } else { @@ -263,7 +313,34 @@ const appProcess = async prms => { } //Ждем ответ от удалённого сервера options.resolveWithFullResponse = true; - let serverResp = await rqp(options); + let serverResp = null; + //Выполняем отправку исходя из протокола + switch (true) { + //Kafka + case sProtocol === objServiceFnSchema.SPROTOCOL_KAFKA: + serverResp = await publishKafka({ + connectionPrms: prms.kafkaConnectionPrms, + url: options.url, + auth: options.auth, + topic: options.topic, + message: options.body + }); + break; + //mqtt и mqtts + case [objServiceFnSchema.SPROTOCOL_MQTT, objServiceFnSchema.SPROTOCOL_MQTTS].includes(sProtocol): + serverResp = await publishMQTT({ + connectionPrms: prms.mqttConnectionPrms, + url: options.url, + auth: options.auth, + topic: options.topic, + message: options.body + }); + break; + //Другие + default: + serverResp = await rqp(options); + break; + } //Сохраняем полученный ответ prms.queue.blResp = Buffer.from(serverResp.body || ""); await dbConn.setQueueResp({ @@ -293,7 +370,7 @@ const appProcess = async prms => { throw new ServerError(SERR_WEB_SERVER, sError); } //Выполняем обработчик "После" (если он есть) - if (prms.function.sAppSrvAfter) { + if (prms.function.sAppSrvAfter && bExecuteAfter) { const fnAfter = getAppSrvFunction(prms.function.sAppSrvAfter); let resAfter = null; try { @@ -515,7 +592,9 @@ const processTask = async prms => { queue: q, service: prms.task.service, function: prms.task.function, - sProxy: prms.task.sProxy + sProxy: prms.task.sProxy, + kafkaConnectionPrms: prms.task.kafkaConnectionPrms, + mqttConnectionPrms: prms.task.mqttConnectionPrms }); //Если результат обработки ошибка - пробрасываем её дальше if (res instanceof ServerError) { diff --git a/core/utils.js b/core/utils.js index 13d309a..011023f 100644 --- a/core/utils.js +++ b/core/utils.js @@ -45,9 +45,7 @@ const validateObject = (obj, schema, sObjName) => { let a = errors.map(e => { return e.message; }); - sRes = `Объект${sObjName ? ` "${sObjName}" ` : " "}имеет некорректный формат: ${_.uniq(a).join( - "; " - )}`; + sRes = `Объект${sObjName ? ` "${sObjName}" ` : " "}имеет некорректный формат: ${_.uniq(a).join("; ")}`; } } else { //Валидатор вернул не то, что мы ожидали @@ -142,8 +140,7 @@ const getAppSrvFunction = sAppSrv => { //Объявим формат (для сообщений об ошибках) const sFormat = "(ожидаемый формат: <МОДУЛЬ>/<ФУНКЦИЯ>)"; //Проверим, что есть что разбирать - if (!sAppSrv) - throw new ServerError(SERR_MODULES_NO_MODULE_SPECIFIED, `Не указаны модуль и функция обработчика ${sFormat}`); + if (!sAppSrv) throw new ServerError(SERR_MODULES_NO_MODULE_SPECIFIED, `Не указаны модуль и функция обработчика ${sFormat}`); //Разбираем try { //Разбираем на модуль и функцию @@ -175,11 +172,7 @@ const getAppSrvFunction = sAppSrv => { const sendMail = prms => { return new Promise((resolve, reject) => { //Проверяем структуру переданного объекта для отправки E-Mail уведомления - let sCheckResult = validateObject( - prms, - prmsUtilsSchema.sendMail, - "Параметры функции отправки E-Mail уведомления" - ); + let sCheckResult = validateObject(prms, prmsUtilsSchema.sendMail, "Параметры функции отправки E-Mail уведомления"); //Если структура объекта в норме if (!sCheckResult) { //Формируем параметры для подключения к SMTP @@ -196,7 +189,7 @@ const sendMail = prms => { transpOptions.auth = { user: prms.mail.sUser, pass: prms.mail.sPass - } + }; } //Настраиваем подключение к SMTP-серверу let transporter = nodemailer.createTransport(transpOptions); @@ -215,12 +208,7 @@ const sendMail = prms => { reject(new ServerError(SERR_MAIL_FAILED, `${error.code}: ${error}`)); } else { if (info.rejected && Array.isArray(info.rejected) && info.rejected.length > 0) { - reject( - new ServerError( - SERR_MAIL_FAILED, - `Сообщение не доствлено адресатам: ${info.rejected.join(", ")}` - ) - ); + reject(new ServerError(SERR_MAIL_FAILED, `Сообщение не доствлено адресатам: ${info.rejected.join(", ")}`)); } else { resolve(info); } @@ -239,7 +227,7 @@ const buildURL = prms => { //Если структура объекта в норме if (!sCheckResult) { //Формируем URL с учетом лишних "/" - return `${prms.sSrvRoot.replace(/\/+$/, '')}/${prms.sFnURL.replace(/^\/+/, '')}${prms.sQuery ? `?${prms.sQuery}` : ""}`; + return `${prms.sSrvRoot.replace(/\/+$/, "")}/${prms.sFnURL.replace(/^\/+/, "")}${prms.sQuery ? `?${prms.sQuery}` : ""}`; } else { throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); } @@ -283,11 +271,7 @@ const parseXML = prms => { //Разбор параметров сообщения/ответа (XML > JSON) const parseOptionsXML = async prms => { //Проверяем структуру переданных параметров - let sCheckResult = validateObject( - prms, - prmsUtilsSchema.parseOptionsXML, - "Параметры функции разбора XML параметров сообщения/ответа" - ); + let sCheckResult = validateObject(prms, prmsUtilsSchema.parseOptionsXML, "Параметры функции разбора XML параметров сообщения/ответа"); //Если структура объекта в норме if (!sCheckResult) { try { @@ -311,11 +295,7 @@ const parseOptionsXML = async prms => { //Сборка параметров сообщения/ответа (JSON > XML) const buildOptionsXML = prms => { //Проверяем структуру переданных параметров - let sCheckResult = validateObject( - prms, - prmsUtilsSchema.buildOptionsXML, - "Параметры функции сборки XML параметров сообщения/ответа" - ); + let sCheckResult = validateObject(prms, prmsUtilsSchema.buildOptionsXML, "Параметры функции сборки XML параметров сообщения/ответа"); //Если структура объекта в норме if (!sCheckResult) { try { @@ -346,6 +326,23 @@ const deepMerge = (...args) => { return res; }; +//Получение брокера Kafka по адресу сервиса обмена +const getKafkaBroker = sURL => { + //Убираем лишние символы + return sURL.slice(8); +}; + +//Получение авторизации для Kafka +const getKafkaAuth = (sUser, sPass) => { + return sUser ? { ssl: true, sasl: { mechanism: "plain", username: sUser, password: sPass } } : null; +}; + +//Получение протокола адреса +const getURLProtocol = sURL => { + //Считываем протокол адреса + return new URL(sURL).protocol.slice(0, -1); +}; + //----------------- // Интерфейс модуля //----------------- @@ -364,3 +361,6 @@ exports.parseOptionsXML = parseOptionsXML; exports.buildOptionsXML = buildOptionsXML; exports.getNowString = getNowString; exports.deepMerge = deepMerge; +exports.getKafkaBroker = getKafkaBroker; +exports.getKafkaAuth = getKafkaAuth; +exports.getURLProtocol = getURLProtocol; diff --git a/models/obj_service_function.js b/models/obj_service_function.js index cf266cb..0be959d 100644 --- a/models/obj_service_function.js +++ b/models/obj_service_function.js @@ -70,6 +70,13 @@ const NERR_NTF_SIGN_YES = 1; //Оповещать об ошибке исполн const SERR_NTF_SIGN_NO = "ERR_NTF_SIGN_NO"; //Не оповещать об ошибке исполнения (строковый код) const SERR_NTF_SIGN_YES = "ERR_NTF_SIGN_YES"; //Оповещать об ошибке исполнения (строковый код) +//Протоколы работы сервиса +const SPROTOCOL_HTTP = "http"; //Протокол HTTP +const SPROTOCOL_HTTPS = "https"; //Протокол HTTPS +const SPROTOCOL_MQTT = "mqtt"; //Протокол MQTT +const SPROTOCOL_MQTTS = "mqtts"; //Протокол MQTTS +const SPROTOCOL_KAFKA = "kafka"; //Протокол для работы с KAFKA + //------------- // Тело модуля //------------- @@ -139,6 +146,11 @@ exports.NERR_NTF_SIGN_NO = NERR_NTF_SIGN_NO; exports.NERR_NTF_SIGN_YES = NERR_NTF_SIGN_YES; exports.SERR_NTF_SIGN_NO = SERR_NTF_SIGN_NO; exports.SERR_NTF_SIGN_YES = SERR_NTF_SIGN_YES; +exports.SPROTOCOL_HTTP = SPROTOCOL_HTTP; +exports.SPROTOCOL_HTTPS = SPROTOCOL_HTTPS; +exports.SPROTOCOL_MQTT = SPROTOCOL_MQTT; +exports.SPROTOCOL_MQTTS = SPROTOCOL_MQTTS; +exports.SPROTOCOL_KAFKA = SPROTOCOL_KAFKA; //Схема валидации функции сервиса exports.ServiceFunction = new Schema({ @@ -156,8 +168,7 @@ exports.ServiceFunction = new Schema({ type: Number, required: true, message: { - type: path => - `Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`, + type: path => `Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`, required: path => `Не указан идентификатор родительского сервиса функции (${path})` } }, @@ -187,8 +198,7 @@ exports.ServiceFunction = new Schema({ enum: [SFN_TYPE_DATA, SFN_TYPE_LOGIN, SFN_TYPE_LOGOUT], required: true, message: { - type: path => - `Строковый код типа функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, + type: path => `Строковый код типа функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, enum: path => `Значение строкового кода типа функции сервиса (${path}) не поддерживается`, required: path => `Не указан строковый код типа функции сервиса (${path})` } @@ -218,8 +228,7 @@ exports.ServiceFunction = new Schema({ ], required: true, message: { - type: path => - `Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, + type: path => `Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, enum: path => `Значение способа передачи параметров функции сервиса (${path}) не поддерживается`, required: path => `Не указан способ передачи параметров функции сервиса (${path})` } @@ -240,10 +249,8 @@ exports.ServiceFunction = new Schema({ ], required: true, message: { - type: path => - `Строковый код способа передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, - enum: path => - `Значение строкового кода способа передачи параметров функции сервиса (${path}) не поддерживается`, + type: path => `Строковый код способа передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, + enum: path => `Значение строкового кода способа передачи параметров функции сервиса (${path}) не поддерживается`, required: path => `Не указан строковый код способа передачи параметров функции сервиса (${path})` } }, @@ -261,8 +268,7 @@ exports.ServiceFunction = new Schema({ ], required: true, message: { - type: path => - `График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, + type: path => `График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, enum: path => `Значение графика повторной отправки запроса функции сервиса (${path}) не поддерживается`, required: path => `Не указан график повторной отправки запроса функции сервиса (${path})` } @@ -283,8 +289,7 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Строковый код графика повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, - enum: path => - `Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`, + enum: path => `Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`, required: path => `Не указан строковый код графика повторной отправки запроса функции сервиса (${path})` } }, @@ -295,8 +300,7 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, - required: path => - `Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})` + required: path => `Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})` } }, //Код типового сообщения обмена, обрабатываемого функцией сервиса @@ -314,8 +318,7 @@ exports.ServiceFunction = new Schema({ type: String, required: false, message: { - type: path => - `Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, + type: path => `Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, required: path => `Не указан обработчик сообщения со стороны БД для функции сервиса (${path})` } }, @@ -327,8 +330,7 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => - `Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`, + required: path => `Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`, validateAppSrvFn: path => `Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>` } @@ -341,8 +343,7 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => - `Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`, + required: path => `Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`, validateAppSrvFn: path => `Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>` } @@ -355,10 +356,8 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Признак необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`, - enum: path => - `Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`, - required: path => - `Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})` + enum: path => `Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`, + required: path => `Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})` } }, //Признак необходимости аутентификации для исполнения функции сервсиа обмена (строковый код) @@ -371,8 +370,7 @@ exports.ServiceFunction = new Schema({ `Строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - String)`, enum: path => `Значение строкового кода признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`, - required: path => - `Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})` + required: path => `Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})` } }, //Признак оповещения об ошибке исполнения сообщения очереди для функции обработки @@ -383,10 +381,8 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - Number)`, - enum: path => - `Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`, - required: path => - `Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})` + enum: path => `Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`, + required: path => `Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})` } }, //Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (строковый код) @@ -399,8 +395,7 @@ exports.ServiceFunction = new Schema({ `Строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`, enum: path => `Значение строкового кода признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`, - required: path => - `Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})` + required: path => `Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})` } }, //Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки @@ -411,8 +406,7 @@ exports.ServiceFunction = new Schema({ message: { type: path => `Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => - `Не указан список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`, + required: path => `Не указан список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`, validateErrNtfMail: path => `Неверный формат списка адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)` } diff --git a/package-lock.json b/package-lock.json index 21f8ef3..46e2f86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", + "kafkajs": "^2.2.4", "lodash": "^4.17.19", "module-alias": "^2.2.2", + "mqtt": "^5.10.1", "nodemailer": "^6.4.11", "oracledb": "^4.2.0", "request": "^2.88.2", @@ -22,6 +24,53 @@ "xml2js": "^0.4.23" } }, + "node_modules/@babel/runtime": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -88,6 +137,25 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -96,6 +164,22 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bl": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.15.tgz", + "integrity": "sha512-RGhjD1XCPS7ZdAH6cEJVaR3gLV4KJP2hvkQ49AH5kwScjiyd0jBM8RsP4oHKzcx+kNCON9752zPeRnuv0HHwzw==", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -121,6 +205,34 @@ "node": ">= 0.8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -145,11 +257,43 @@ "node": ">= 0.8" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, "node_modules/component-type": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", "integrity": "sha1-ikeQFwAjjk/DIml3EjAibyS0Fak=" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -280,6 +424,22 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -343,6 +503,18 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -426,6 +598,11 @@ "node": ">=6" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -466,6 +643,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -489,6 +685,15 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -523,11 +728,24 @@ "verror": "1.10.0" } }, + "node_modules/kafkajs": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz", + "integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -579,11 +797,102 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" }, + "node_modules/mqtt": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz", + "integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt-packet/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mqtt/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -605,6 +914,36 @@ "node": ">=6.0.0" } }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/number-allocator/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/number-allocator/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -659,6 +998,19 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -714,6 +1066,31 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -785,6 +1162,11 @@ "node": ">=0.6" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -847,6 +1229,14 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -887,6 +1277,33 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -907,6 +1324,11 @@ "node": ">=0.8" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -940,6 +1362,16 @@ "resolved": "https://registry.npmjs.org/typecast/-/typecast-0.0.1.tgz", "integrity": "sha1-//t13La98d744pO2tuiT1sHtGd4=" }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -956,6 +1388,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1006,6 +1443,57 @@ "extsprintf": "^1.2.0" } }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -1028,6 +1516,47 @@ } }, "dependencies": { + "@babel/runtime": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "requires": { + "undici-types": "~6.19.2" + } + }, + "@types/readable-stream": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "requires": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "requires": { + "@types/node": "*" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -1081,6 +1610,11 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1089,6 +1623,24 @@ "tweetnacl": "^0.14.3" } }, + "bl": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.15.tgz", + "integrity": "sha512-RGhjD1XCPS7ZdAH6cEJVaR3gLV4KJP2hvkQ49AH5kwScjiyd0jBM8RsP4oHKzcx+kNCON9752zPeRnuv0HHwzw==", + "requires": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1111,6 +1663,20 @@ "type-is": "~1.6.17" } }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1129,11 +1695,39 @@ "delayed-stream": "~1.0.0" } }, + "commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" + }, "component-type": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", "integrity": "sha1-ikeQFwAjjk/DIml3EjAibyS0Fak=" }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1236,6 +1830,16 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -1293,6 +1897,15 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "requires": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1354,6 +1967,11 @@ "har-schema": "^2.0.0" } }, + "help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -1384,6 +2002,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -1404,6 +2027,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -1435,11 +2063,21 @@ "verror": "1.10.0" } }, + "kafkajs": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz", + "integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==" + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1473,11 +2111,79 @@ "mime-db": "1.47.0" } }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, "module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" }, + "mqtt": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz", + "integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==", + "requires": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "mqtt-packet": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", + "requires": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1493,6 +2199,30 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.0.tgz", "integrity": "sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg==" }, + "number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "requires": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -1531,6 +2261,16 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1571,6 +2311,28 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -1624,6 +2386,11 @@ "lodash": "^4.17.19" } }, + "rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1682,6 +2449,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -1708,6 +2480,21 @@ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1722,6 +2509,11 @@ "punycode": "^2.1.1" } }, + "tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -1749,6 +2541,16 @@ "resolved": "https://registry.npmjs.org/typecast/-/typecast-0.0.1.tgz", "integrity": "sha1-//t13La98d744pO2tuiT1sHtGd4=" }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1762,6 +2564,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1797,6 +2604,43 @@ "extsprintf": "^1.2.0" } }, + "worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "requires": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", diff --git a/package.json b/package.json index 25eed3c..deec4a9 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", + "kafkajs": "^2.2.4", "lodash": "^4.17.19", "module-alias": "^2.2.2", + "mqtt": "^5.10.1", "nodemailer": "^6.4.11", "oracledb": "^4.2.0", "request": "^2.88.2",