From d6beebabc5c6414498628d4becf6fdc6d030e384 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Mon, 10 Dec 2018 15:59:32 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20E-mail=20=D0=B0=D0=B4=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D0=BE=D0=B2=20=D0=B2=20=D0=BE=D0=B1=D1=89=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D1=8B=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B2=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B4=D0=B0=D1=86=D0=B8=D0=B8,=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20E-mail=20?= =?UTF-8?q?=D0=B2=20utils.js=20(=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=B5=D0=BD=D0=BD=D0=BE=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D1=81=20=D1=81=D1=85=D0=B5=D0=BC=20=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8),=20=D0=B2?= =?UTF-8?q?=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=20E-mail=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B2=20=D1=81=D1=85=D0=B5=D0=BC=D0=B5=20Servic?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/service_available_controller.js | 58 +------------- core/utils.js | 86 ++++++++++++++++++++- models/common.js | 34 ++++++++ models/obj_service.js | 15 +++- models/prms_service_available_controller.js | 32 -------- models/prms_utils.js | 68 ++++++++++++++++ 6 files changed, 205 insertions(+), 88 deletions(-) create mode 100644 models/common.js create mode 100644 models/prms_utils.js diff --git a/core/service_available_controller.js b/core/service_available_controller.js index 16f41b7..c323113 100644 --- a/core/service_available_controller.js +++ b/core/service_available_controller.js @@ -10,10 +10,9 @@ const _ = require("lodash"); //Работа с массивами и коллекциями const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами const EventEmitter = require("events"); //Обработчик пользовательских событий -const nodemailer = require("nodemailer"); //Отправка E-Mail сообщений const { ServerError } = require("./server_errors"); //Типовая ошибка -const { SERR_SERVICE_UNAVAILABLE, SERR_MAIL_FAILED, SERR_OBJECT_BAD_INTERFACE } = require("./constants"); //Общесистемные константы -const { makeErrorText, validateObject } = require("./utils"); //Вспомогательные функции +const { SERR_SERVICE_UNAVAILABLE, SERR_OBJECT_BAD_INTERFACE } = require("./constants"); //Общесистемные константы +const { makeErrorText, validateObject, sendMail } = require("./utils"); //Вспомогательные функции const prmsServiceAvailableControllerSchema = require("../models/prms_service_available_controller"); //Схемы валидации параметров функций класса const objServiceSchema = require("../models/obj_service"); //Схемы валидации сервисов @@ -79,56 +78,6 @@ class ServiceAvailableController extends EventEmitter { //Оповестим подписчиков об останове this.emit(SEVT_SERVICE_AVAILABLE_CONTROLLER_STOPPED); } - //Отправка E-Mail уведомления о недоступности сервиса - sendUnAvailableMail(prms) { - return new Promise((resolve, reject) => { - //Проверяем структуру переданного объекта для старта - let sCheckResult = validateObject( - prms, - prmsServiceAvailableControllerSchema.sendUnAvailableMail, - "Параметры функции отправки E-Mail уведомления о недоступности удалённого сервиса" - ); - //Если структура объекта в норме - if (!sCheckResult) { - //Параметры подключения к SMTP-серверу - let transporter = nodemailer.createTransport({ - host: this.mail.sHost, - port: this.mail.nPort, - secure: this.mail.nPort == 465, - auth: { - user: this.mail.sUser, - pass: this.mail.sPass - } - }); - //Параметры отправляемого сообщения - let mailOptions = { - from: this.mail.sFrom, - to: prms.sTo, - subject: prms.sSubject, - text: prms.sMessage - }; - //Отправляем сообщение - transporter.sendMail(mailOptions, (error, info) => { - if (error) { - reject(new ServerError(SERR_MAIL_FAILED, `${error.code}: ${error.response}`)); - } else { - if (info.rejected && Array.isArray(info.rejected) && info.rejected.length > 0) { - reject( - new ServerError( - SERR_MAIL_FAILED, - `Сообщение не доствлено адресатам: ${info.rejected.join(", ")}` - ) - ); - } else { - resolve(info); - } - } - }); - } else { - reject(new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult)); - } - }); - } //Перезапуск опроса списка сервисов async restartDetectingLoop() { //Включаем опрос сервисов только если установлен флаг работы @@ -197,7 +146,8 @@ class ServiceAvailableController extends EventEmitter { }`; await this.logger.error(sMessage, { nServiceId: this.services[i].nId }); try { - await this.sendUnAvailableMail({ + await sendMail({ + mail: this.mail, sTo: this.services[i].sUnavlblNtfMail, sSubject, sMessage diff --git a/core/utils.js b/core/utils.js index e22c27d..0c2560c 100644 --- a/core/utils.js +++ b/core/utils.js @@ -9,13 +9,17 @@ const _ = require("lodash"); //Работа с массивами и объектами const Schema = require("validate"); //Схемы валидации +const nodemailer = require("nodemailer"); //Отправка E-Mail сообщений const { SERR_UNEXPECTED, SMODULES_PATH_MODULES, + SERR_OBJECT_BAD_INTERFACE, SERR_MODULES_NO_MODULE_SPECIFIED, - SERR_MODULES_BAD_INTERFACE + SERR_MODULES_BAD_INTERFACE, + SERR_MAIL_FAILED } = require("./constants"); //Глобавльные константы системы const { ServerError } = require("./server_errors"); //Ошибка сервера +const prmsUtilsSchema = require("../models/prms_utils"); //Схемы валидации параметров функций //------------ // Тело модуля @@ -25,12 +29,17 @@ const { ServerError } = require("./server_errors"); //Ошибка сервер const validateObject = (obj, schema, sObjName) => { //Объявим результат let sRes = ""; + //Если пришла верная схема if (schema instanceof Schema) { + //И есть что проверять if (obj) { + //Сделаем это const objTmp = _.cloneDeep(obj); const errors = schema.validate(objTmp, { strip: false }); + //Если есть ошибки if (errors && Array.isArray(errors)) { if (errors.length > 0) { + //Сформируем из них сообщение об ошибке валидации let a = errors.map(e => { return e.message; }); @@ -41,12 +50,15 @@ const validateObject = (obj, schema, sObjName) => { _.uniq(a).join("; "); } } else { + //Валидатор вернул не то, что мы ожидали sRes = "Неожиданный ответ валидатора"; } } else { + //Нам не передали объект на проверку sRes = "Объект" + (sObjName ? " '" + sObjName + "' " : " ") + "не указан"; } } else { + //Пришла не схема валидации а непонятно что sRes = "Ошибочный формат схемы валидации"; } //Вернем результат @@ -55,50 +67,70 @@ const validateObject = (obj, schema, sObjName) => { //Формирование полного пути к подключаемому модулю const makeModuleFullPath = sModuleName => { + //Если имя модуля передано if (sModuleName) { + //Объединим его с шаблоном пути до библиотеки модулей return SMODULES_PATH_MODULES + "/" + sModuleName; } else { + //Нет имени модуля - нет полного пути return ""; } }; //Формирование текста ошибки const makeErrorText = e => { + //Сообщение об ошибке по умолчанию let sErr = `${SERR_UNEXPECTED}: ${e.message}`; + //Если это наше внутреннее сообщение, с кодом, то сделаем ошибку более информативной if (e instanceof ServerError) sErr = `${e.sCode}: ${e.sMessage}`; + //Вернем ответ return sErr; }; //Считывание наименования модуля-обработчика сервера приложений (ожидаемый формат - <МОДУЛЬ>/<ФУНКЦИЯ>) const getAppSrvModuleName = sAppSrv => { + //Если есть что разбирать if (sAppSrv) { + //И если это строка if (sAppSrv instanceof String || typeof sAppSrv === "string") { + //Проверим наличие разделителя между именем модуля и функции if (sAppSrv.indexOf("/") === -1) { + //Нет разделителя - нечего вернуть return null; } else { + //Вернём всё, что левее разделителя return sAppSrv.substring(0, sAppSrv.indexOf("/")); } } else { + //Пришла не строка return null; } } else { + //Ничего не пришло return null; } }; //Считывание наименования функции модуля-обработчика сервера приложений (ожидаемый формат - <МОДУЛЬ>/<ФУНКЦИЯ>) const getAppSrvFunctionName = sAppSrv => { + //Если есть что разбирать if (sAppSrv) { + //И если это строка if (sAppSrv instanceof String || typeof sAppSrv === "string") { + //Проверим наличие разделителя между именем модуля и функции if (sAppSrv.indexOf("/") === -1) { + //Нет разделителя - нечего вернуть return null; } else { + //Вернём всё, что правее разделителя return sAppSrv.substring(sAppSrv.indexOf("/") + 1, sAppSrv.length); } } else { + //Пришла не строка return null; } } else { + //Ничего не пришло return null; } }; @@ -139,6 +171,57 @@ const getAppSrvFunction = sAppSrv => { } }; +//Отправка E-Mail уведомления +const sendMail = prms => { + return new Promise((resolve, reject) => { + //Проверяем структуру переданного объекта для старта + let sCheckResult = validateObject( + prms, + prmsUtilsSchema.sendMail, + "Параметры функции отправки E-Mail уведомления" + ); + //Если структура объекта в норме + if (!sCheckResult) { + //Параметры подключения к SMTP-серверу + let transporter = nodemailer.createTransport({ + host: prms.mail.sHost, + port: prms.mail.nPort, + secure: prms.mail.nPort == 465, + auth: { + user: prms.mail.sUser, + pass: prms.mail.sPass + } + }); + //Параметры отправляемого сообщения + let mailOptions = { + from: prms.mail.sFrom, + to: prms.sTo, + subject: prms.sSubject, + text: prms.sMessage + }; + //Отправляем сообщение + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + reject(new ServerError(SERR_MAIL_FAILED, `${error.code}: ${error.response}`)); + } else { + if (info.rejected && Array.isArray(info.rejected) && info.rejected.length > 0) { + reject( + new ServerError( + SERR_MAIL_FAILED, + `Сообщение не доствлено адресатам: ${info.rejected.join(", ")}` + ) + ); + } else { + resolve(info); + } + } + }); + } else { + reject(new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult)); + } + }); +}; + //----------------- // Интерфейс модуля //----------------- @@ -149,3 +232,4 @@ exports.makeErrorText = makeErrorText; exports.getAppSrvModuleName = getAppSrvModuleName; exports.getAppSrvFunctionName = getAppSrvFunctionName; exports.getAppSrvFunction = getAppSrvFunction; +exports.sendMail = sendMail; diff --git a/models/common.js b/models/common.js new file mode 100644 index 0000000..6b9d34e --- /dev/null +++ b/models/common.js @@ -0,0 +1,34 @@ +/* + Сервис интеграции ПП Парус 8 с WEB API + Модели данных: общие функции валидации, константы, модели +*/ + +//------------ +// Тело модуля +//------------ + +//Валидация списка адресов E-Mail +const validateMailList = sMailList => { + //Если есть что валидировать + if (sMailList) { + //Объявим разделитель списка адресов + let sSpr = ","; + //Регулярное выражение для контроля адреса E-Mail + let sMailRegExp = /^(([A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+@[a-z0-9_-]+(\.[a-z0-9_-]+)*\.[a-z]+)/; + //Развалим строку с разделителями на массив адресов + let addrs = sMailList.split(sSpr); + //Обходим массив адресов + for (i = 0; i < addrs.length; i++) { + //Проверяем адрес на соответствие регулярному выражению + if (!sMailRegExp.test(addrs[i])) return false; + } + } + //Если мы здесь - валидация прошла успешно + return true; +}; + +//----------------- +// Интерфейс модуля +//----------------- + +exports.validateMailList = validateMailList; diff --git a/models/obj_service.js b/models/obj_service.js index d57d67c..b3cb750 100644 --- a/models/obj_service.js +++ b/models/obj_service.js @@ -9,6 +9,7 @@ const Schema = require("validate"); //Схемы валидации const { defServiceFunctions } = require("./obj_service_functions"); //Схема валидации списка функций сервиса +const { validateMailList } = require("./common"); //Общие объекты валидации моделей данных //---------- // Константы @@ -26,6 +27,15 @@ const NUNAVLBL_NTF_SIGN_YES = 1; //Оповещать о простое const SUNAVLBL_NTF_SIGN_NO = "UNAVLBL_NTF_NO"; //Не оповещать о простое (строковый код) const SUNAVLBL_NTF_SIGN_YES = "UNAVLBL_NTF_YES"; //Оповещать о простое (строковый код) +//------------- +// Тело модуля +//------------- + +//Валидация списка адресов E-Mail для оповещения о простое внешнего сервиса +const validateUnavlblNtfMail = val => { + return validateMailList(val); +}; + //------------------ // Интерфейс модуля //------------------ @@ -151,10 +161,13 @@ exports.Service = new Schema({ sUnavlblNtfMail: { type: String, required: false, + use: { validateUnavlblNtfMail }, message: { type: "Список адресов E-Mail для оповещения о простое внешнего сервиса (sUnavlblNtfMail) имеет некорректный тип данных (ожидалось - String)", - required: "Не указан список адресов E-Mail для оповещения о простое внешнего сервиса (sUnavlblNtfMail)" + required: "Не указан список адресов E-Mail для оповещения о простое внешнего сервиса (sUnavlblNtfMail)", + validateUnavlblNtfMail: + "Неверный формат списка адресов E-Mail для оповещения о простое внешнего сервиса (sUnavlblNtfMail), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)" } }, //Список функций сервиса diff --git a/models/prms_service_available_controller.js b/models/prms_service_available_controller.js index 6b3bf3a..c61bee9 100644 --- a/models/prms_service_available_controller.js +++ b/models/prms_service_available_controller.js @@ -38,38 +38,6 @@ exports.ServiceAvailableController = new Schema({ } }); -//Схема валидации параметров функции отправки E-Mail уведомления о недоступности сервиса -exports.sendUnAvailableMail = new Schema({ - //Список адресов E-Mail для отправки уведомления - sTo: { - type: String, - required: true, - message: { - type: path => - `Список адресов E-Mail для отправки уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => `Не указан cписок адресов E-Mail для отправки уведомления (${path})` - } - }, - //Заголовок сообщения - sSubject: { - type: String, - required: true, - message: { - type: path => `Заголовок сообщения (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => `Не указан заголовок сообщения (${path})` - } - }, - //Текст уведомления - sMessage: { - type: String, - required: true, - message: { - type: path => `Текст уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`, - required: path => `Не указан текст уведомления (${path})` - } - } -}); - //Схема валидации параметров функции запуска контроллера exports.startController = new Schema({ //Список обслуживаемых сервисов diff --git a/models/prms_utils.js b/models/prms_utils.js new file mode 100644 index 0000000..fff2051 --- /dev/null +++ b/models/prms_utils.js @@ -0,0 +1,68 @@ +/* + Сервис интеграции ПП Парус 8 с WEB API + Модели данных: описатели параметров вспомогательных функций (модуль utils.js) +*/ + +//---------------------- +// Подключение библиотек +//---------------------- + +const Schema = require("validate"); //Схемы валидации +const { mail } = require("./obj_config"); //Схемы валидации конфигурации сервера приложений +const { validateMailList } = require("./common"); //Общие объекты валидации моделей данных + +//------------- +// Тело модуля +//------------- + +//Валидация списка адресов E-Mail для отправки уведомления +const validateTo = val => { + return validateMailList(val); +}; + +//------------------ +// Интерфейс модуля +//------------------ + +//Схема валидации параметров функции отправки E-Mail +exports.sendMail = new Schema({ + //Параметры отправки E-Mail уведомлений + mail: { + schema: mail, + required: true, + message: { + required: path => `Не указаны параметры отправки E-Mail уведомлений (${path})` + } + }, + //Список адресов E-Mail для отправки уведомления + sTo: { + type: String, + required: true, + use: { validateTo }, + message: { + type: path => + `Список адресов E-Mail для отправки уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`, + required: path => `Не указан cписок адресов E-Mail для отправки уведомления (${path})`, + validateTo: path => + `Неверный формат списка адресов E-Mail для отправки уведомления (${path}), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)` + } + }, + //Заголовок сообщения + sSubject: { + type: String, + required: true, + message: { + type: path => `Заголовок сообщения (${path}) имеет некорректный тип данных (ожидалось - String)`, + required: path => `Не указан заголовок сообщения (${path})` + } + }, + //Текст уведомления + sMessage: { + type: String, + required: true, + message: { + type: path => `Текст уведомления (${path}) имеет некорректный тип данных (ожидалось - String)`, + required: path => `Не указан текст уведомления (${path})` + } + } +});