Compare commits

...

20 Commits

Author SHA1 Message Date
Mikhail Chechnev
380df706d8 ЦИТК-790: Косметика в комментах 2026-03-11 18:48:17 +03:00
Mim
d432a5d02e ЦИТК-986, ЦИТК-790 - ДИАДОК/СБИС - Параметр "Загружать все документы" и время жизни токена доступа для СБИС
Reviewed-on: CITKParus/P8-ExchangeService#17
2026-03-11 18:44:02 +03:00
Mikhail Chechnev
4e1b1b010b ЦИТК-795: CodeReview - http_client - удален лишний onError для secureSocket, из интерфейса удалён экспорт HttpError (не используется) 2026-03-11 15:42:54 +03:00
Mikhail Chechnev
5af4b9d498 ЦИТК-795: CodeReview - http_client - комментарии по тексту модуля, приведение к стандартному code-style 2026-03-11 15:38:34 +03:00
Mikhail Chechnev
b2b7d01342 ЦИТК-795: CodeReview - deepMerge в стрелочном стиле, дополнительные комментарии по коду 2026-03-11 15:36:20 +03:00
Mikhail Chechnev
72feca2a38 Инициализация Thick-режима для расширения "Импорт ГАР" с учетом типа данных переменной окружения 2026-03-10 16:25:46 +03:00
Mikhail Chechnev
207d2ada07 Флаг Thin-режима и указание пути для клиента Oracle переведены в config.js, контроль доступности пула подключений Oracle при создании соединения 2026-03-07 00:55:15 +03:00
9d8207417a ЦИТК-790 2026-03-06 13:22:30 +03:00
5d93f31b77 ЦИТК-790 2026-03-06 13:20:15 +03:00
f6bdb12463 Обновить modules/diadoc.js 2026-03-06 12:51:46 +03:00
b156ed7556 Merge branch '2.0.0_NodeJS_22' 2026-02-13 12:19:57 +03:00
Mikhail Chechnev
c15c06bf33 Релиз 2026.02.10 2026-02-09 19:54:16 +03:00
Mim
3dcba310f6 ЦИТК-1018 - Фиксация в очереди идентификатора сервера интеграции, обработавшего входящее сообщение
Reviewed-on: CITKParus/P8-ExchangeService#14
2026-02-09 19:29:57 +03:00
Dollerino
24283ccd80 ЦИТК-1018 - Указание во входящем сообщении обрабатывающий сервис интеграции 2026-02-04 12:09:41 +03:00
Mikhail Chechnev
ac7870e884 ЦИТК-1032 - приоритетный подбор контрагента по коду участника ЭДО 2026-02-02 13:50:50 +03:00
Mikhail Chechnev
3cb48c11a0 Релиз 2025.12.24 2025-12-26 17:42:27 +03:00
Mikhail Chechnev
94932decfe ЦИТК-1012 - Исправлены условия консольной выдачи ошибок отправки исходящих MQTT сообщений 2025-12-26 17:23:05 +03:00
Mim
a13682507f ЦИТК-1012 - Добавлены параметры протоколирования для брокеров MQTT/KAFKA, устранена ошибка подключения с использующимся sGroupId
Reviewed-on: CITKParus/P8-ExchangeService#13
2025-12-26 17:15:23 +03:00
Dollerino
1dc5b5d108 ЦИТК-1012 - Добавлены параметры протоколирования для брокеров MQTT/KAFKA, устранена ошибка подключения с использующимся sGroupId 2025-12-25 20:32:01 +03:00
Dollerino
b573b16000 ЦИТК-1012 - Добавлены параметры протоколирования для брокеров MQTT/KAFKA, устранена ошибка подключения с использующимся sGroupId 2025-12-08 15:49:30 +03:00
11 changed files with 585 additions and 523 deletions

View File

@ -7,16 +7,6 @@
// Тело модуля // Тело модуля
//------------ //------------
//Переменные окружения
/*
Только для СУБД Oracle.
1. NODE_ORACLE_DB_THIN_MODE - Установите значение "1", чтобы использовать режим "тонкого клиента" (Thin-режим)
при подключении к БД (не требует установки Oracle Client).
2. ORACLE_CLIENT_LIB_DIR - Путь к директории с библиотеками Oracle Client,
используется только для работы в режиме "толстого клиента" (Thick-режим), если значение не указано,
то будет использоваться значение из переменной операционной системы PATH.
*/
//Общие параметры //Общие параметры
let common = { let common = {
//Наименование сервера приложений //Наименование сервера приложений
@ -24,7 +14,7 @@ let common = {
//Версия сервера приложений //Версия сервера приложений
sVersion: "8.5.6.1", sVersion: "8.5.6.1",
//Релиз сервера приложений //Релиз сервера приложений
sRelease: "2026.02.10", sRelease: "2026.02.12",
//Таймаут останова сервера (мс) //Таймаут останова сервера (мс)
nTerminateTimeout: 60000, nTerminateTimeout: 60000,
//Контролировать версию Системы //Контролировать версию Системы
@ -44,7 +34,11 @@ let dbConnect = {
//Наименование сервера приложений в сессии БД //Наименование сервера приложений в сессии БД
sSessionAppName: "PARUS$ExchangeServer", sSessionAppName: "PARUS$ExchangeServer",
//Подключаемый модуль обслуживания БД (низкоуровневые функции работы с СУБД) //Подключаемый модуль обслуживания БД (низкоуровневые функции работы с СУБД)
sConnectorModule: "parus_oracle_db.js" sConnectorModule: "parus_oracle_db.js",
//Применение "тонкого" (Thin) или "толстого" (Thick) режима подключения (false - Thick-режим - работа через установленного клиента СУБД Oracle, true - Thin-режим - не требует установки клиента СУБД Oracle, допустим только для Oracle >= 12.1)
bOraUseThinMode: false,
//Путь к домашней директории Oracle Client (только для Thick-режима подключения к СУБД Oracle, если значение не указано, то будет использоваться значение из переменной окружения "PATH" ОС)
sOraClient: ""
}; };
//Параметры обработки очереди исходящих сообщений //Параметры обработки очереди исходящих сообщений

View File

@ -118,7 +118,6 @@ class ParusAppServer {
await this.logger.info( await this.logger.info(
`Обработчик очереди входящих сообщений запущен (порт - ${nPort}, доступные IP - ${sHost === "0.0.0.0" ? getIPs().join("; ") : sHost})` `Обработчик очереди входящих сообщений запущен (порт - ${nPort}, доступные IP - ${sHost === "0.0.0.0" ? getIPs().join("; ") : sHost})`
); );
//Запускаем
//Запускаем модуль отправки уведомлений //Запускаем модуль отправки уведомлений
await this.logger.info("Запуск модуля отправки уведомлений..."); await this.logger.info("Запуск модуля отправки уведомлений...");
try { try {

File diff suppressed because it is too large Load Diff

View File

@ -30,8 +30,9 @@ const { SPROTOCOL_HTTP, SPROTOCOL_KAFKA } = require("../models/obj_service"); //
//Глубокое клонирование //Глубокое клонирование
const deepClone = value => { const deepClone = value => {
//Клонируем объект в зависимости от его типа, сохраняя прототипы //Буфер для фиксации "клонированных" элементов исходного объекта
const seen = new WeakMap(); const seen = new WeakMap();
//Функция рекурсивного клонирования (разбирает тип полученного значения и в зависимости от него выполняет клонирование, сохраняя прототипы)
const clone = val => { const clone = val => {
//Примитивы и функции возвращаем как есть //Примитивы и функции возвращаем как есть
if (val === null || typeof val !== "object") return val; if (val === null || typeof val !== "object") return val;
@ -104,10 +105,12 @@ const deepClone = value => {
}); });
return obj; return obj;
}; };
//Выполняем копирование рекурсивно с верхнего уровня
try { try {
//Возвращаем что получилось
return clone(value); return clone(value);
} catch (e) { } catch (e) {
//В случае непредвиденной ошибки формируем информативное исключение //В случае непредвиденной ошибки формируем исключение
const err = new Error("Ошибка глубокого копирования объекта"); const err = new Error("Ошибка глубокого копирования объекта");
err.originalError = e; err.originalError = e;
throw err; throw err;
@ -407,30 +410,33 @@ const getNowString = () => {
}; };
//Глубокое слияние объектов //Глубокое слияние объектов
function deepMerge(...sources) { const deepMerge = (...sources) => {
//Проверка на простой объект (не имеет специального поведения или прототипа)
const isPlainObject = value => Object.prototype.toString.call(value) === "[object Object]"; const isPlainObject = value => Object.prototype.toString.call(value) === "[object Object]";
//Клоникарование значения
const cloneValue = value => { const cloneValue = value => deepClone(value);
return deepClone(value); //Буфер для результата
};
const target = {}; const target = {};
//Обходим входные параметры по очереди
for (const source of sources) { for (const source of sources) {
//Пропускаем непростые объекты - сливать можем только простые
if (!isPlainObject(source)) continue; if (!isPlainObject(source)) continue;
//Обходим пары "ключ-значение"
for (const [key, value] of Object.entries(source)) { for (const [key, value] of Object.entries(source)) {
//Если значение ключа - объект
if (isPlainObject(value)) { if (isPlainObject(value)) {
//Сливаем рекурсивно
if (!isPlainObject(target[key])) target[key] = {}; if (!isPlainObject(target[key])) target[key] = {};
target[key] = deepMerge(target[key], value); target[key] = deepMerge(target[key], value);
} else { } else {
//Примитивы - просто клонируем
target[key] = cloneValue(value); target[key] = cloneValue(value);
} }
} }
} }
//Возвращаем собранный буфер, содержащий все слитые воедино объекты-параметры
return target; return target;
} };
//Глубокое копирование объекта //Глубокое копирование объекта
const deepCopyObject = obj => JSON.parse(JSON.stringify(obj)); const deepCopyObject = obj => JSON.parse(JSON.stringify(obj));
@ -509,27 +515,31 @@ const getURLProtocol = sURL => {
//Обёртывание промиса в таймаут исполнения //Обёртывание промиса в таймаут исполнения
const wrapPromiseTimeout = (timeout, executor) => { const wrapPromiseTimeout = (timeout, executor) => {
//Проверяем входные параметры - должен быть указан ненулевой таймаут и функция-исполнитель промиса
if (!timeout || typeof executor !== "function") { if (!timeout || typeof executor !== "function") {
//Разрешаем сразу, не ожидая
return executor ? executor() : Promise.resolve(); return executor ? executor() : Promise.resolve();
} }
//Параметры прошли проверку, создаем экхемпляр контроллера прерывания асинхронного процесса
const controller = new AbortController(); const controller = new AbortController();
//Подготовим объект для выбрасывания ошибки
const sMessage = `Истёк интервал ожидания (${timeout} мс) завершения асинхронного процесса.`; const sMessage = `Истёк интервал ожидания (${timeout} мс) завершения асинхронного процесса.`;
const timeoutError = new Error(sMessage); const timeoutError = new Error(sMessage);
timeoutError.error = sMessage; timeoutError.error = sMessage;
//Буфер для идентификатора таймера
let timeoutPid; let timeoutPid;
//Создаём промис с взведенным таумером на у казанный таймаут, промис будет "отклонён" по истечении таймера, но перед этим контроллер прерывания выдаст "сигнал" на отмену осинхронного процесса
const timeoutPromise = new Promise((_, reject) => { const timeoutPromise = new Promise((_, reject) => {
timeoutPid = setTimeout(() => { timeoutPid = setTimeout(() => {
controller.abort(timeoutError); controller.abort(timeoutError);
reject(timeoutError); reject(timeoutError);
}, timeout); }, timeout);
}); });
//Создаём промис с функцией-исполнителем асинхронного процесса
const taskPromise = Promise.resolve(executor(controller.signal)); const taskPromise = Promise.resolve(executor(controller.signal));
//Запускаем оба промиса в режиме "гонка" - кто "быстрее" исполнится
return Promise.race([taskPromise, timeoutPromise]).finally(() => { return Promise.race([taskPromise, timeoutPromise]).finally(() => {
//В любом случае не забываем зачистить взведённый таймер
if (timeoutPid) clearTimeout(timeoutPid); if (timeoutPid) clearTimeout(timeoutPid);
}); });
}; };

View File

@ -23,7 +23,13 @@ let appSrv = new app.ParusAppServer(); //Экземпляр сервера пр
//---------------------------------------- //----------------------------------------
//Разрешение на TLS (Transport Layer Security) без авторизации //Разрешение на TLS (Transport Layer Security) без авторизации
process.env.NODE_TLS_REJECT_UNAUTHORIZED = cfg.outGoing.bValidateSSL === false ? "0" : "1"; process.env.NODE_TLS_REJECT_UNAUTHORIZED = cfg?.outGoing?.bValidateSSL === false ? "0" : "1";
//Включение "Токого режима" для Oracle
process.env.NODE_ORACLE_DB_THIN_MODE = cfg?.dbConnect?.bOraUseThinMode === true ? "1" : "0";
//Путь к клиентским библиотекам для Oracle
process.env.ORACLE_CLIENT_LIB_DIR = cfg?.dbConnect?.sOraClient || "";
//Обработка события "выход" жизненного цикла процесса //Обработка события "выход" жизненного цикла процесса
process.on("exit", code => { process.on("exit", code => {

View File

@ -189,6 +189,24 @@ const dbConnect = new Schema({
type: path => `Наименование подключаемого модуля обслуживания БД (${path}) имеет некорректный тип данных (ожидалось - String)`, type: path => `Наименование подключаемого модуля обслуживания БД (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указано наименование подключаемого модуля обслуживания БД (${path})` required: path => `Не указано наименование подключаемого модуля обслуживания БД (${path})`
} }
},
//Признак применения "тонкого" (Thin) или "толстого" (Thick) режима подключения к БД
bOraUseThinMode: {
type: Boolean,
required: true,
message: {
type: path => `Признак режима подключения (Thin/Thick) к БД (${path}) имеет некорректный тип данных (ожидалось - Boolean)`,
required: path => `Не указан признак режима подключения (Thin/Thick) к БД (${path})`
}
},
//Путь к домашней директории Oracle Client
sOraClient: {
type: String,
required: false,
message: {
type: path => `Путь к домашней директории Oracle Client (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указан путь к домашней директории Oracle Client (${path})`
}
} }
}); });

View File

@ -533,6 +533,10 @@ const beforeEvent = async prms => {
if (prms.options.sdepartment_name && sDepartmentId) { if (prms.options.sdepartment_name && sDepartmentId) {
surl = `${surl}&departmentId=${sDepartmentId}`; surl = `${surl}&departmentId=${sDepartmentId}`;
} }
//Заполним максимальное количество возвращаемых элементов
if (prms.options.limit) {
surl = `${surl}&limit=${prms.options.limit}`;
}
} else { } else {
if (prms.queue.blMsg) { if (prms.queue.blMsg) {
//Конвертируем XML из "Парус 8" в понятный "ДИАДОК" JSON //Конвертируем XML из "Парус 8" в понятный "ДИАДОК" JSON

View File

@ -20,7 +20,7 @@ const sax = require("./node_modules/sax"); //Событийный XML-парсе
//Инициализируем Thick-режим до любых подключений к БД //Инициализируем Thick-режим до любых подключений к БД
try { try {
if (typeof oracledb.initOracleClient === "function" && !(process.env.NODE_ORACLE_DB_THIN_MODE === 1)) { if (typeof oracledb.initOracleClient === "function" && !(process.env.NODE_ORACLE_DB_THIN_MODE === "1")) {
const libDir = process.env.ORACLE_CLIENT_LIB_DIR; const libDir = process.env.ORACLE_CLIENT_LIB_DIR;
if (libDir) { if (libDir) {
oracledb.initOracleClient({ libDir }); oracledb.initOracleClient({ libDir });

View File

@ -16,7 +16,7 @@ const { makeErrorText } = require("../core/utils"); //Вспомогательн
//Инициализируем Thick-режим до любых подключений к БД //Инициализируем Thick-режим до любых подключений к БД
try { try {
if (typeof oracledb.initOracleClient === "function" && !(process.env.NODE_ORACLE_DB_THIN_MODE === 1)) { if (typeof oracledb.initOracleClient === "function" && !(process.env.NODE_ORACLE_DB_THIN_MODE === "1")) {
const libDir = process.env.ORACLE_CLIENT_LIB_DIR; const libDir = process.env.ORACLE_CLIENT_LIB_DIR;
if (libDir) { if (libDir) {
oracledb.initOracleClient({ libDir }); oracledb.initOracleClient({ libDir });
@ -203,9 +203,10 @@ const clearServer = async prms => {
//Подключение к БД //Подключение к БД
const connect = async prms => { const connect = async prms => {
let pool = null;
try { try {
//Создаем пул подключения //Создаем пул подключения
let pool = await oracledb.createPool({ pool = await oracledb.createPool({
user: prms.sUser, user: prms.sUser,
password: prms.sPassword, password: prms.sPassword,
connectString: prms.sConnectString, connectString: prms.sConnectString,
@ -229,8 +230,15 @@ const connect = async prms => {
}); });
} }
}); });
//Проверяем доступность пула
let tstConn = await pool.getConnection();
await tstConn.close();
//Всё ок - возвращяем его
return pool; return pool;
} catch (e) { } catch (e) {
//Закрываем пул, если успели открыть
if (pool) pool.close(NPOOL_DRAIN_TIME);
//Возвращаем ошибку
throw new Error(e.message); throw new Error(e.message);
} }
}; };

View File

@ -9,7 +9,7 @@
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
const { httpRequest } = require("../core/http_client"); //Работа с HTTP/HTTPS запросами const { httpRequest } = require("../core/http_client"); //Работа с HTTP/HTTPS запросами
const { SMCHD_STORAGE_SYSTEM } = require("./sbis_config"); //Система хранения МЧД const { SMCHD_STORAGE_SYSTEM, NCTX_EXP } = require("./sbis_config"); //Параметры работы расширения
//--------------------- //---------------------
// Глобальные константы // Глобальные константы
@ -138,7 +138,7 @@ const afterConnect = async prms => {
return { return {
blResp: Buffer.from(resp.result), blResp: Buffer.from(resp.result),
sCtx: resp.result, sCtx: resp.result,
dCtxExp: addHours(new Date(), 23) dCtxExp: addHours(new Date(), NCTX_EXP)
}; };
} else { } else {
throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resp.error.message ? resp.error.message : "Неожиданная ошибка"}`); throw new Error(`Сервер ЭДО "СБИС" вернул ошибку: ${resp.error.message ? resp.error.message : "Неожиданная ошибка"}`);

View File

@ -9,3 +9,6 @@
//Система хранения МЧД //Система хранения МЧД
exports.SMCHD_STORAGE_SYSTEM = "https://m4d.nalog.gov.ru/EMCHD/check-status"; exports.SMCHD_STORAGE_SYSTEM = "https://m4d.nalog.gov.ru/EMCHD/check-status";
//Время жизни токена аутентификации (в часах)
exports.NCTX_EXP = 20;