diff --git a/config.js b/config.js
index 2ab47f0..c86ddc7 100644
--- a/config.js
+++ b/config.js
@@ -71,6 +71,44 @@ let inComing = {
nPoolIncrement: 0
};
+//Параметры подключения к Kafka
+const kafka = [
+ {
+ //Мнемокод сервиса обмена (пусто - использовать по умолчанию)
+ sService: "",
+ //ID клиента-отправителя
+ sClientIdSender: "Parus",
+ //ID клиента-получателя
+ sClientIdRecipient: "Parus",
+ //Группа получателя
+ sGroupId: "Parus",
+ //Время ожидания успешного подключения (мс)
+ nConnectionTimeout: 5000,
+ //Необходимость попытки переподключения при потере соединения
+ bRestartOnFailure: true,
+ //Время максимального ожидания между попытками переподключения (мс)
+ nMaxRetryTime: 20000,
+ //Время ожидания между попытками переподключения (мс)
+ nInitialRetryTime: 10000
+ }
+];
+
+//Параметры подключения к MQTT
+const mqtt = [
+ {
+ //Мнемокод сервиса обмена (пусто - использовать по умолчанию)
+ sService: "",
+ //ID клиента-отправителя
+ sClientIdSender: "Parus",
+ //ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться)
+ sClientIdRecipient: "ParusRecipient",
+ //Время ожидания успешного подключения (мс)
+ nConnectTimeout: 5000,
+ //Время ожидания между попытками переподключения (мс)
+ nReconnectPeriod: 10000
+ }
+];
+
//Параметры отправки E-Mail уведомлений
let mail = {
//Адреc сервера SMTP
@@ -98,5 +136,7 @@ module.exports = {
dbConnect,
outGoing,
inComing,
+ kafka,
+ mqtt,
mail
};
diff --git a/config_default.js b/config_default.js
index 4d2e174..c289e71 100644
--- a/config_default.js
+++ b/config_default.js
@@ -71,6 +71,44 @@ let inComing = {
nPoolIncrement: 0
};
+//Параметры подключения к Kafka
+const kafka = [
+ {
+ //Мнемокод сервиса обмена (пусто - использовать по умолчанию)
+ sService: "",
+ //ID клиента-отправителя
+ sClientIdSender: "Parus",
+ //ID клиента-получателя
+ sClientIdRecipient: "Parus",
+ //Группа получателя
+ sGroupId: "Parus",
+ //Время ожидания успешного подключения (мс)
+ nConnectionTimeout: 5000,
+ //Необходимость попытки переподключения при потере соединения
+ bRestartOnFailure: true,
+ //Время максимального ожидания между попытками переподключения (мс)
+ nMaxRetryTime: 20000,
+ //Время ожидания между попытками переподключения (мс)
+ nInitialRetryTime: 10000
+ }
+];
+
+//Параметры подключения к MQTT
+const mqtt = [
+ {
+ //Мнемокод сервиса обмена (пусто - использовать по умолчанию)
+ sService: "",
+ //ID клиента-отправителя
+ sClientIdSender: "Parus",
+ //ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться)
+ sClientIdRecipient: "ParusRecipient",
+ //Время ожидания успешного подключения (мс)
+ nConnectTimeout: 5000,
+ //Время ожидания между попытками переподключения (мс)
+ nReconnectPeriod: 10000
+ }
+];
+
//Параметры отправки E-Mail уведомлений
let mail = {
//Адреc сервера SMTP
@@ -98,5 +136,7 @@ module.exports = {
dbConnect,
outGoing,
inComing,
+ kafka,
+ mqtt,
mail
};
diff --git a/core/app.js b/core/app.js
index 77f3e4e..c6d096e 100644
--- a/core/app.js
+++ b/core/app.js
@@ -96,7 +96,7 @@ class ParusAppServer {
//Запускаем обслуживание очереди входящих
await this.logger.info("Запуск обработчика очереди входящих сообщений...");
try {
- this.inQ.startProcessing({ services: this.services });
+ await this.inQ.startProcessing({ services: this.services });
} catch (e) {
await this.logger.error(`Ошибка запуска обработчика очереди входящих сообщений: ${makeErrorText(e)}`);
await this.stop();
@@ -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,
+ kafka: prms.config.kafka,
+ mqtt: prms.config.mqtt
});
//Создаём обработчик очереди входящих
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,
+ kafka: prms.config.kafka,
+ mqtt: prms.config.mqtt
});
//Создаём контроллер доступности удалённых сервисов
this.srvAvlCtrl = new sac.ServiceAvailableController({
diff --git a/core/in_queue.js b/core/in_queue.js
index cb2f519..ce20127 100644
--- a/core/in_queue.js
+++ b/core/in_queue.js
@@ -13,7 +13,18 @@ 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,
+ getKafkaConnectionSettings,
+ getMQTTConnectionSettings,
+ getURLProtocol
+} = 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 +39,8 @@ const {
SERR_DB_SERVER,
SERR_UNAUTH
} = require("./constants"); //Общесистемные константы
+const { subscribeMQTT } = require("./mqtt_connector"); //Модуль для работы с MQTT
+const { subscribeKafka } = require("./kafka_connector"); //Модуль для работы с Kafka
//--------------------------
// Глобальные идентификаторы
@@ -71,6 +84,13 @@ class InQueue extends EventEmitter {
this.webApp.options("*", cors());
//WEB-сервер
this.srv = null;
+ //Параметры подключения к Kafka
+ this.kafka = prms.kafka;
+ //Параметры подключения к MQTT
+ this.mqtt = prms.mqtt;
+ //Внешние подключения
+ this.kafkaConnections = [];
+ this.mqttConnections = [];
} else {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
}
@@ -360,8 +380,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
+ };
+ //Кладём сообщение в очередь
+ q = await this.dbConn.putQueue({
+ nServiceFnId: fn.nId,
+ sOptions: buildOptionsXML({ options }),
+ blMsg
+ });
+ //Скажем что пришло новое входящее сообщение
+ 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) {
+ async startProcessing(prms) {
//Проверяем структуру переданного объекта для старта
let sCheckResult = validateObject(prms, prmsInQueueSchema.startProcessing, "Параметры функции запуска обработки очереди входящих сообщений");
//Если структура объекта в норме
@@ -379,55 +717,136 @@ class InQueue extends EventEmitter {
//Конфигурируем сервер - обработка тела сообщения
this.webApp.use(bodyParser.raw({ limit: `${this.inComing.nMsgMaxSize}mb`, type: "*/*" }));
//Конфигурируем сервер - обходим все сервисы, работающие на приём сообщений
- _.forEach(_.filter(this.services, { nSrvType: objServiceSchema.NSRV_TYPE_RECIVE }), srvs => {
- //Для любых запросов к корневому адресу сервиса - ответ о том, что это за сервис, и что он работает
- this.webApp.all(srvs.sSrvRoot, (req, res) => {
- res.status(200).send(
- `
Сервер приложений ПП Парус 8
(${this.common.sVersion} релиз ${this.common.sRelease})
Сервис: ${srvs.sName}
`
+ _.forEach(
+ _.filter(this.services, srv => {
+ return (
+ srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE &&
+ [objServiceSchema.SPROTOCOL_HTTP, objServiceSchema.SPROTOCOL_HTTPS].includes(getURLProtocol(srv.sSrvRoot))
);
- });
- //Для всех статических функций сервиса...
- _.forEach(
- _.filter(srvs.functions, fn => fn.sFnURL.startsWith("@")),
- fn => {
- this.webApp.use(
- buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL.substr(1) }),
- express.static(`${this.inComing.sStaticDir}/${fn.sFnURL.substr(1)}`)
+ }),
+ srvs => {
+ //Для любых запросов к корневому адресу сервиса - ответ о том, что это за сервис, и что он работает
+ this.webApp.all(srvs.sSrvRoot, (req, res) => {
+ res.status(200).send(
+ `
Сервер приложений ПП Парус 8
(${this.common.sVersion} релиз ${this.common.sRelease})
Сервис: ${srvs.sName}
`
);
- }
- );
- //Для всех функций сервиса (кроме статических)...
- _.forEach(
- _.filter(srvs.functions, fn => !fn.sFnURL.startsWith("@")),
- fn => {
- //...собственный обработчик, в зависимости от указанного способа передачи параметров
- this.webApp[fn.sFnPrmsType.toLowerCase()](buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL }), async (req, res) => {
- try {
- //Вызываем обработчик
- await this.processMessage({ req, res, service: srvs, function: fn });
- } catch (e) {
+ });
+ //Для всех статических функций сервиса...
+ _.forEach(
+ _.filter(srvs.functions, fn => fn.sFnURL.startsWith("@")),
+ fn => {
+ this.webApp.use(
+ buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL.substr(1) }),
+ express.static(`${this.inComing.sStaticDir}/${fn.sFnURL.substr(1)}`)
+ );
+ }
+ );
+ //Для всех функций сервиса (кроме статических)...
+ _.forEach(
+ _.filter(srvs.functions, fn => !fn.sFnURL.startsWith("@")),
+ fn => {
+ //...собственный обработчик, в зависимости от указанного способа передачи параметров
+ this.webApp[fn.sFnPrmsType.toLowerCase()](buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL }), async (req, res) => {
+ try {
+ //Вызываем обработчик
+ await this.processMessage({ req, res, service: srvs, function: fn });
+ } catch (e) {
+ //Протоколируем в журнал работы сервера
+ await this.logger.error(makeErrorText(e), {
+ nServiceId: srvs.nId,
+ nServiceFnId: fn.nId
+ });
+ //Отправим ошибку клиенту
+ res.status(500).send(makeErrorText(e));
+ }
+ });
+ //...и собственный обработчик ошибок
+ this.webApp.use(buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL }), async (err, req, res, next) => {
//Протоколируем в журнал работы сервера
- await this.logger.error(makeErrorText(e), {
+ await this.logger.error(makeErrorText(new ServerError(SERR_WEB_SERVER, err.message)), {
nServiceId: srvs.nId,
nServiceFnId: fn.nId
});
//Отправим ошибку клиенту
- res.status(500).send(makeErrorText(e));
- }
- });
- //...и собственный обработчик ошибок
- this.webApp.use(buildURL({ sSrvRoot: srvs.sSrvRoot, sFnURL: fn.sFnURL }), async (err, req, res, next) => {
- //Протоколируем в журнал работы сервера
- await this.logger.error(makeErrorText(new ServerError(SERR_WEB_SERVER, err.message)), {
- nServiceId: srvs.nId,
- nServiceFnId: fn.nId
+ res.status(500).send(makeErrorText(new ServerError(SERR_WEB_SERVER, err.message)));
});
- //Отправим ошибку клиенту
- res.status(500).send(makeErrorText(new ServerError(SERR_WEB_SERVER, err.message)));
- });
+ }
+ );
+ }
+ );
+ //Инициализируем настройки подключения
+ let connectionSettings = null;
+ //Считываем прием сообщений по Kafka
+ let kafkaSrvs = this.services.filter(srv => {
+ return srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE && getURLProtocol(srv.sSrvRoot) === objServiceSchema.SPROTOCOL_KAFKA;
+ });
+ //Если есть сервисы с приемом сообщений по Kafka
+ if (kafkaSrvs.length !== 0) {
+ //Обходим данные сервисы
+ for (let srv of kafkaSrvs) {
+ //Если у сервиса обмена есть функции
+ if (srv.functions.length !== 0) {
+ //Считываем настройки подключения к Kafka
+ connectionSettings = getKafkaConnectionSettings(srv.sCode, this.kafka);
+ //Если настройки подключения считаны
+ if (connectionSettings) {
+ //Подключаемся и подписываемся на соответствующий брокер
+ let connectionKafka = await subscribeKafka({
+ settings: connectionSettings,
+ service: srv,
+ processKafkaMessage: prms => this.processKafkaMessage(prms),
+ logger: this.logger
+ });
+ //Если подключение было создано
+ if (connectionKafka) {
+ //Добавляем в общий список подключений kafka
+ this.kafkaConnections.push(connectionKafka);
+ }
+ } else {
+ await this.logger.error(
+ `Ошибка получения настроек подключения к Kafka для сервиса "${srv.sCode}". Необходимо проверить соответствующий параметр ("kafka") файла конфигурации сервиса приложений ("config.js").`
+ );
+ }
}
+ }
+ }
+ //Считываем прием сообщений по MQTT
+ let mqttSrvs = this.services.filter(srv => {
+ return (
+ srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE &&
+ [objServiceSchema.SPROTOCOL_MQTT, objServiceSchema.SPROTOCOL_MQTTS].includes(getURLProtocol(srv.sSrvRoot))
);
});
+ //Если есть сервисы с приемом сообщений по MQTT
+ if (mqttSrvs.length !== 0) {
+ //Обходим данные сервисы
+ for (let srv of mqttSrvs) {
+ //Если у сервиса обмена есть функции
+ if (srv.functions.length !== 0) {
+ //Считываем настройки подключения к MQTT
+ connectionSettings = getMQTTConnectionSettings(srv.sCode, this.mqtt);
+ //Если настройки подключения считаны
+ if (connectionSettings) {
+ //Подключаемся и подписываемся на соответствующий брокер
+ let connectionMQTT = await subscribeMQTT({
+ settings: connectionSettings,
+ service: srv,
+ processMQTTMessage: prms => this.processMQTTMessage(prms),
+ logger: this.logger
+ });
+ //Если подключение было создано
+ if (connectionMQTT) {
+ //Добавляем в общий список подключений kafka
+ this.mqttConnections.push(connectionMQTT);
+ }
+ } else {
+ await this.logger.error(
+ `Ошибка получения настроек подключения к MQTT для сервиса "${srv.sCode}". Необходимо проверить соответствующий параметр ("mqtt") файла конфигурации сервиса приложений ("config.js").`
+ );
+ }
+ }
+ }
+ }
//Запросы на адреса, не входящие в состав объявленных сервисов - 404 NOT FOUND
this.webApp.use("*", (req, res) => {
res.status(404).send(
@@ -453,10 +872,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..5504fa6
--- /dev/null
+++ b/core/kafka_connector.js
@@ -0,0 +1,119 @@
+/*
+ Сервис интеграции ПП Парус 8 с WEB API
+ Модуль ядра: обработчик kafka сообщений
+*/
+
+//----------------------
+// Подключение библиотек
+//----------------------
+
+const { makeErrorText, getKafkaBroker, getKafkaAuth } = require("./utils"); //Вспомогательные функции
+const { Kafka, logLevel } = require("kafkajs"); //Работа с Kafka
+
+//------------
+// Тело модуля
+//------------
+
+//Отправка сообщения Kafka
+const publishKafka = async ({ settings, url, auth, topic, message }) => {
+ //Иницализируем подключение к Kafka
+ let kafka = new Kafka({
+ clientId: settings.sClientIdSender,
+ brokers: [url],
+ connectionTimeout: settings.nConnectionTimeout,
+ logLevel: logLevel.NOTHING,
+ ...auth
+ });
+ //Инициализируем продюсера
+ let producer = kafka.producer();
+ //Подключаемся к Kafka
+ await producer.connect();
+ //Отправляем сообщение
+ await producer.send({ topic: topic, messages: [{ value: message }] });
+ //Отключаемся
+ await producer.disconnect();
+ //Возвращаем статус успешной отправки
+ return { statusCode: 200 };
+};
+
+//Получение MQTT сообщений
+const subscribeKafka = async ({ settings, service, processKafkaMessage, logger }) => {
+ try {
+ //Признак необходимости вывода сообщения о потере соединения
+ let bLogLostConnection = true;
+ //Получаем брокера по URL сервиса
+ let sBroker = getKafkaBroker(service.sSrvRoot);
+ //Иницализируем подключение к Kafka
+ let client = new Kafka({
+ clientId: settings.sClientIdRecipient,
+ brokers: [sBroker],
+ connectionTimeout: settings.nConnectionTimeout,
+ ...getKafkaAuth(service.sSrvUser, service.sSrvPass),
+ logLevel: logLevel.NOTHING,
+ retry: {
+ retries: 0,
+ maxRetryTime: settings.nMaxRetryTime,
+ initialRetryTime: settings.nInitialRetryTime,
+ restartOnFailure: error => {
+ return new Promise(resolve => {
+ //Если требуется вывести ошибку
+ if (bLogLostConnection) {
+ //Выводим ошибку
+ logger.error(`Соединение с Kafka потеряно (${sBroker}): ${makeErrorText(error)}`);
+ //Сбрасываем признак необходимости вывода ошибки
+ bLogLostConnection = false;
+ }
+ resolve(settings.bRestartOnFailure);
+ });
+ }
+ }
+ });
+ //Инициализируем получателя
+ let consumer = client.consumer({ groupId: settings.sGroupId });
+ //Устанавливаем прослушивание
+ await consumer.connect();
+ consumer.subscribe({
+ topics: service.functions.map(fn => {
+ return fn.sFnURL;
+ })
+ });
+ //Запускаем прослушивание необходимых топиков
+ consumer.run({
+ eachMessage: async ({ topic, message }) => {
+ try {
+ //Вызываем обработчик
+ processKafkaMessage({
+ message,
+ service,
+ fn: service.functions.find(fn => {
+ return fn.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..17915f5
--- /dev/null
+++ b/core/mqtt_connector.js
@@ -0,0 +1,87 @@
+/*
+ Сервис интеграции ПП Парус 8 с WEB API
+ Модуль ядра: обработчик mqtt сообщений
+*/
+
+//----------------------
+// Подключение библиотек
+//----------------------
+
+const { makeErrorText } = require("./utils"); //Вспомогательные функции
+const mqtt = require("mqtt"); //Работа с MQTT
+
+//------------
+// Тело модуля
+//------------
+
+//Отправка MQTT сообщения
+const publishMQTT = async ({ settings, url, auth, topic, message }) => {
+ //Инициализируем подключение
+ const client = await mqtt.connectAsync(url, {
+ clientId: settings.sClientIdSender,
+ clean: true,
+ connectTimeout: settings.nConnectTimeout,
+ username: auth.user,
+ password: auth.pass,
+ reconnectPeriod: settings.nReconnectPeriod
+ });
+ //Отправляем сообщение
+ await client.publishAsync(topic, message);
+ //Закрываем подключение
+ await client.endAsync();
+ //Возвращаем статус успешной отправки
+ return { statusCode: 200 };
+};
+
+//Получение MQTT сообщений
+const subscribeMQTT = async ({ settings, service, processMQTTMessage, logger }) => {
+ try {
+ //Инициализируем строку подключения
+ let sBroker = service.sSrvRoot;
+ //Инициализируем подключение
+ const client = await mqtt.connectAsync(sBroker, {
+ clientId: settings.sClientIdRecipient,
+ clean: true,
+ connectTimeout: settings.nConnectTimeout,
+ username: service.sSrvUser,
+ password: service.sSrvPass,
+ reconnectPeriod: settings.nReconnectPeriod
+ });
+ //Обходим функции сервиса
+ service.functions.forEach(fn => {
+ client.subscribe(fn.sFnURL);
+ });
+ //Прослушиваем сообщения
+ client.on("message", (topic, message) => {
+ //Обрабатываем сообщение
+ processMQTTMessage({
+ message,
+ service,
+ fn: service.functions.find(fn => {
+ return fn.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..fcab507 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.kafka = prms.kafka;
+ //Параметры подключения к MQTT
+ this.mqtt = prms.mqtt;
} 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,
+ kafka: this.kafka,
+ mqtt: this.mqtt
});
//Уменьшаем количество доступных обработчиков
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..1383fb1 100644
--- a/core/out_queue_processor.js
+++ b/core/out_queue_processor.js
@@ -12,7 +12,20 @@ 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,
+ getKafkaConnectionSettings,
+ getMQTTConnectionSettings,
+ 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"); //Схема валидации параметров функций модуля
@@ -20,6 +33,7 @@ const objQueueSchema = require("../models/obj_queue"); //Схемы валида
const objServiceSchema = require("../models/obj_service"); //Схемы валидации сервиса
const objServiceFnSchema = require("../models/obj_service_function"); //Схемы валидации функции сервиса
const {
+ SERR_UNEXPECTED,
SERR_OBJECT_BAD_INTERFACE,
SERR_APP_SERVER_BEFORE,
SERR_APP_SERVER_AFTER,
@@ -28,6 +42,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 +145,71 @@ 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 === objServiceSchema.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);
+ options.settings = getKafkaConnectionSettings(prms.service.sCode, prms.kafka);
+ //Если параметры подключения не считаны
+ if (!options.settings) {
+ //Расскажем об ошибке считывания
+ throw new ServerError(
+ SERR_UNEXPECTED,
+ `Ошибка получения настроек подключения к Kafka для сервиса "${prms.service.sCode}". Необходимо проверить соответствующий параметр ("kafka") файла конфигурации сервиса приложений ("config.js").`
+ );
+ }
+ //Указываем, что выполнение обработчика "После" невозможно
+ bExecuteAfter = false;
+ break;
+ //mqtt и mqtts
+ case [objServiceSchema.SPROTOCOL_MQTT, objServiceSchema.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 };
+ options.settings = getMQTTConnectionSettings(prms.service.sCode, prms.mqtt);
+ //Если параметры подключения не считаны
+ if (!options.settings) {
+ //Расскажем об ошибке считывания
+ throw new ServerError(
+ SERR_UNEXPECTED,
+ `Ошибка получения настроек подключения к MQTT для сервиса "${prms.service.sCode}". Необходимо проверить соответствующий параметр ("mqtt") файла конфигурации сервиса приложений ("config.js").`
+ );
+ }
+ //Указываем, что выполнение обработчика "После" невозможно
+ 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 +233,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 +265,10 @@ 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) ||
+ [objServiceSchema.SPROTOCOL_KAFKA, objServiceSchema.SPROTOCOL_MQTT, objServiceSchema.SPROTOCOL_MQTTS].includes(
+ sProtocol
+ )
) {
options.body = prms.queue.blMsg;
} else {
@@ -261,9 +330,39 @@ const appProcess = async prms => {
nQueueId: prms.queue.nId
});
}
- //Ждем ответ от удалённого сервера
- options.resolveWithFullResponse = true;
- let serverResp = await rqp(options);
+ //Инициализируем ответ от сервера
+ let serverResp = null;
+ //Выполняем отправку исходя из протокола
+ switch (true) {
+ //Kafka
+ case sProtocol === objServiceSchema.SPROTOCOL_KAFKA:
+ serverResp = await publishKafka({
+ settings: options.settings,
+ url: options.url,
+ auth: options.auth,
+ topic: options.topic,
+ message: options.body
+ });
+ console.log(serverResp);
+ break;
+ //mqtt и mqtts
+ case [objServiceSchema.SPROTOCOL_MQTT, objServiceSchema.SPROTOCOL_MQTTS].includes(sProtocol):
+ serverResp = await publishMQTT({
+ settings: options.settings,
+ url: options.url,
+ auth: options.auth,
+ topic: options.topic,
+ message: options.body
+ });
+ break;
+ //Другие
+ default:
+ //Ждем ответ от удалённого сервера
+ options.resolveWithFullResponse = true;
+ //Отправляем запрос
+ serverResp = await rqp(options);
+ break;
+ }
//Сохраняем полученный ответ
prms.queue.blResp = Buffer.from(serverResp.body || "");
await dbConn.setQueueResp({
@@ -293,7 +392,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 +614,9 @@ const processTask = async prms => {
queue: q,
service: prms.task.service,
function: prms.task.function,
- sProxy: prms.task.sProxy
+ sProxy: prms.task.sProxy,
+ kafka: prms.task.kafka,
+ mqtt: prms.task.mqtt
});
//Если результат обработки ошибка - пробрасываем её дальше
if (res instanceof ServerError) {
diff --git a/core/utils.js b/core/utils.js
index 13d309a..e4973d4 100644
--- a/core/utils.js
+++ b/core/utils.js
@@ -22,6 +22,7 @@ const {
} = require("./constants"); //Глобавльные константы системы
const { ServerError } = require("./server_errors"); //Ошибка сервера
const prmsUtilsSchema = require("../models/prms_utils"); //Схемы валидации параметров функций
+const { SPROTOCOL_HTTP, SPROTOCOL_KAFKA } = require("../models/obj_service"); //Схемы валидации сервиса
//------------
// Тело модуля
@@ -45,9 +46,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 +141,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 +173,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 +190,7 @@ const sendMail = prms => {
transpOptions.auth = {
user: prms.mail.sUser,
pass: prms.mail.sPass
- }
+ };
}
//Настраиваем подключение к SMTP-серверу
let transporter = nodemailer.createTransport(transpOptions);
@@ -215,12 +209,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 +228,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 +272,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 +296,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 +327,62 @@ const deepMerge = (...args) => {
return res;
};
+//Считывание параметров подключения для сервиса обмена (при service === "" считывание подключения "По умолчанию", settingsArray - массив объектов [{sService: "", ...},...])
+const getConnectionSettings = (service, settingsArray) => {
+ //Считываем параметры и возвращаем
+ return settingsArray.find(connection => {
+ return connection.sService === service;
+ });
+};
+
+//Считывание параметров подключения к Kafka для сервиса обмена (kafka - массив объектов [{sService: "", ...},...])
+const getKafkaConnectionSettings = (service, kafka) => {
+ //Считываем подключение с указанным сервисом обмена
+ let kafkaConnection = getConnectionSettings(service, kafka);
+ //Если нет подключения с указанным сервисом обмена
+ if (!kafkaConnection) {
+ //Считываем "По умолчанию"
+ kafkaConnection = getConnectionSettings("", kafka);
+ }
+ //Вернем результат
+ return kafkaConnection;
+};
+
+//Считывание параметров подключения к MQTT для сервиса обмена (mqtt - массив объектов [{sService: "", ...},...])
+const getMQTTConnectionSettings = (service, mqtt) => {
+ //Считываем подключение с указанным сервисом обмена
+ let mqttConnection = getConnectionSettings(service, mqtt);
+ //Если нет подключения с указанным сервисом обмена
+ if (!mqttConnection) {
+ //Считываем "По умолчанию"
+ mqttConnection = getConnectionSettings("", mqtt);
+ }
+ //Вернем результат
+ return mqttConnection;
+};
+
+//Получение брокера Kafka по адресу сервиса обмена (прим. kafka://server.ru -> server.ru, https://server.ru => undefined)
+const getKafkaBroker = sURL => {
+ //Если протокол URL - Kafka
+ if (getURLProtocol(sURL) === SPROTOCOL_KAFKA) {
+ //Возвращаем брокера
+ return sURL.slice(8);
+ }
+ //Возвращаем undefined
+ return;
+};
+
+//Получение авторизации для Kafka
+const getKafkaAuth = (sUser, sPass) => {
+ return sUser ? { ssl: true, sasl: { mechanism: "plain", username: sUser, password: sPass } } : null;
+};
+
+//Получение протокола адреса (прим. mqtt://server.ru -> mqtt, https://server.ru => https, ...)
+const getURLProtocol = sURL => {
+ //Если начинается с "/" - HTTP, иначе получаем из URL
+ return sURL.substring(0, 1) === "/" ? SPROTOCOL_HTTP : new URL(sURL).protocol.slice(0, -1);
+};
+
//-----------------
// Интерфейс модуля
//-----------------
@@ -364,3 +401,8 @@ exports.parseOptionsXML = parseOptionsXML;
exports.buildOptionsXML = buildOptionsXML;
exports.getNowString = getNowString;
exports.deepMerge = deepMerge;
+exports.getKafkaConnectionSettings = getKafkaConnectionSettings;
+exports.getMQTTConnectionSettings = getMQTTConnectionSettings;
+exports.getKafkaBroker = getKafkaBroker;
+exports.getKafkaAuth = getKafkaAuth;
+exports.getURLProtocol = getURLProtocol;
diff --git a/models/obj_service.js b/models/obj_service.js
index 5d86db2..598c0fa 100644
--- a/models/obj_service.js
+++ b/models/obj_service.js
@@ -33,6 +33,13 @@ const NIS_AUTH_NO = 0; //Неаутентифицирован
const SIS_AUTH_YES = "IS_AUTH_YES"; //Аутентифицирован (строковый код)
const SIS_AUTH_NO = "IS_AUTH_NO"; //Неаутентифицирован (строковый код)
+//Протоколы работы сервиса
+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
+
//-------------
// Тело модуля
//-------------
@@ -59,6 +66,11 @@ exports.NIS_AUTH_YES = NIS_AUTH_YES;
exports.NIS_AUTH_NO = NIS_AUTH_NO;
exports.SIS_AUTH_YES = SIS_AUTH_YES;
exports.SIS_AUTH_NO = SIS_AUTH_NO;
+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.Service = new Schema({
@@ -144,10 +156,8 @@ exports.Service = new Schema({
enum: [NUNAVLBL_NTF_SIGN_NO, NUNAVLBL_NTF_SIGN_YES],
required: true,
message: {
- type: path =>
- `Признак необходимости оповещения о простое внешнего сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
- enum: path =>
- `Значение признака необходимости оповещения о простое внешнего сервиса (${path}) не поддерживается`,
+ type: path => `Признак необходимости оповещения о простое внешнего сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ enum: path => `Значение признака необходимости оповещения о простое внешнего сервиса (${path}) не поддерживается`,
required: path => `Не указан признак необходимости оповещения о простое внешнего сервиса (${path})`
}
},
@@ -159,10 +169,8 @@ exports.Service = new Schema({
message: {
type: path =>
`Строковый код признака необходимости оповещения о простое внешнего сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
- enum: path =>
- `Значение строкового кода признака необходимости оповещения о простое внешнего сервиса (${path}) не поддерживается`,
- required: path =>
- `Не указан строковый код признака необходимости оповещения о простое внешнего сервиса (${path})`
+ enum: path => `Значение строкового кода признака необходимости оповещения о простое внешнего сервиса (${path}) не поддерживается`,
+ required: path => `Не указан строковый код признака необходимости оповещения о простое внешнего сервиса (${path})`
}
},
//Максимальное время простоя (мин) удалённого сервиса для генерации оповещения
@@ -172,8 +180,7 @@ exports.Service = new Schema({
message: {
type: path =>
`Максимальное время простоя (мин) удалённого сервиса для генерации оповещения (${path}) имеет некорректный тип данных (ожидалось - Number)`,
- required: path =>
- `Не указано максимальное время простоя (мин) удалённого сервиса для генерации оповещения (${path})`
+ required: path => `Не указано максимальное время простоя (мин) удалённого сервиса для генерации оповещения (${path})`
}
},
//Список адресов E-Mail для оповещения о простое внешнего сервиса
@@ -194,8 +201,7 @@ exports.Service = new Schema({
type: String,
required: false,
message: {
- type: path =>
- `Адрес прокси-сервера в очереди обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
+ type: path => `Адрес прокси-сервера в очереди обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указан адрес прокси-сервера в очереди обмена (${path})`
}
},
@@ -237,8 +243,7 @@ exports.ServiceCtx = new Schema({
type: String,
required: false,
message: {
- type: path =>
- `Строковое представление даты истечения контекста (${path}) имеет некорректный тип данных (ожидалось - String)`,
+ type: path => `Строковое представление даты истечения контекста (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указано строковое представление даты истечения контекста (${path})`
}
},
@@ -248,8 +253,7 @@ exports.ServiceCtx = new Schema({
enum: [NIS_AUTH_YES, NIS_AUTH_NO],
required: true,
message: {
- type: path =>
- `Признака аутентицированности сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ type: path => `Признака аутентицированности сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => `Значение признака аутентицированности сервиса (${path}) не поддерживается`,
required: path => `Не указан признак аутентицированности сервиса (${path})`
}
@@ -260,8 +264,7 @@ exports.ServiceCtx = new Schema({
enum: [SIS_AUTH_YES, SIS_AUTH_NO],
required: true,
message: {
- type: path =>
- `Строковый код признака аутентицированности сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
+ type: path => `Строковый код признака аутентицированности сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path => `Значение строкового кода признака аутентицированности сервиса (${path}) не поддерживается`,
required: path => `Не указан строковый код признака аутентицированности сервиса (${path})`
}
@@ -284,8 +287,7 @@ exports.ServiceExpiredQueueInfo = new Schema({
type: Number,
required: true,
message: {
- type: path =>
- `Количество просроченных сообщений обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ type: path => `Количество просроченных сообщений обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`,
required: path => `Не указано количество просроченных сообщений обмена (${path})`
}
},
@@ -294,8 +296,7 @@ exports.ServiceExpiredQueueInfo = new Schema({
type: String,
required: true,
message: {
- type: path =>
- `Информация о просроченных сообщениях обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
+ type: path => `Информация о просроченных сообщениях обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указана информация о просроченных сообщениях обмена (${path})`
}
}
diff --git a/models/obj_service_function.js b/models/obj_service_function.js
index cf266cb..a8e24c3 100644
--- a/models/obj_service_function.js
+++ b/models/obj_service_function.js
@@ -156,8 +156,7 @@ exports.ServiceFunction = new Schema({
type: Number,
required: true,
message: {
- type: path =>
- `Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ type: path => `Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`,
required: path => `Не указан идентификатор родительского сервиса функции (${path})`
}
},
@@ -187,8 +186,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 +216,7 @@ exports.ServiceFunction = new Schema({
],
required: true,
message: {
- type: path =>
- `Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ type: path => `Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => `Значение способа передачи параметров функции сервиса (${path}) не поддерживается`,
required: path => `Не указан способ передачи параметров функции сервиса (${path})`
}
@@ -240,10 +237,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 +256,7 @@ exports.ServiceFunction = new Schema({
],
required: true,
message: {
- type: path =>
- `График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
+ type: path => `График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => `Значение графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
required: path => `Не указан график повторной отправки запроса функции сервиса (${path})`
}
@@ -283,8 +277,7 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Строковый код графика повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
- enum: path =>
- `Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
+ enum: path => `Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
required: path => `Не указан строковый код графика повторной отправки запроса функции сервиса (${path})`
}
},
@@ -295,8 +288,7 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
- required: path =>
- `Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})`
+ required: path => `Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})`
}
},
//Код типового сообщения обмена, обрабатываемого функцией сервиса
@@ -314,8 +306,7 @@ exports.ServiceFunction = new Schema({
type: String,
required: false,
message: {
- type: path =>
- `Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
+ type: path => `Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указан обработчик сообщения со стороны БД для функции сервиса (${path})`
}
},
@@ -327,8 +318,7 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
- required: path =>
- `Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`,
+ required: path => `Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`,
validateAppSrvFn: path =>
`Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>`
}
@@ -341,8 +331,7 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
- required: path =>
- `Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`,
+ required: path => `Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`,
validateAppSrvFn: path =>
`Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>`
}
@@ -355,10 +344,8 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Признак необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`,
- enum: path =>
- `Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`,
- required: path =>
- `Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
+ enum: path => `Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`,
+ required: path => `Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
}
},
//Признак необходимости аутентификации для исполнения функции сервсиа обмена (строковый код)
@@ -371,8 +358,7 @@ exports.ServiceFunction = new Schema({
`Строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path =>
`Значение строкового кода признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`,
- required: path =>
- `Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
+ required: path => `Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
}
},
//Признак оповещения об ошибке исполнения сообщения очереди для функции обработки
@@ -383,10 +369,8 @@ exports.ServiceFunction = new Schema({
message: {
type: path =>
`Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - Number)`,
- enum: path =>
- `Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`,
- required: path =>
- `Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
+ enum: path => `Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`,
+ required: path => `Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
}
},
//Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (строковый код)
@@ -399,8 +383,7 @@ exports.ServiceFunction = new Schema({
`Строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path =>
`Значение строкового кода признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`,
- required: path =>
- `Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
+ required: path => `Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
}
},
//Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки
@@ -411,8 +394,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",