Очередь исходящих, запуск и останов сервера приложений
This commit is contained in:
parent
edda9c0e3b
commit
f354847515
@ -14,7 +14,7 @@ let dbConnect = {
|
|||||||
//Пароль пользователя БД
|
//Пароль пользователя БД
|
||||||
sPassword: "parus",
|
sPassword: "parus",
|
||||||
//Строка подключения к БД
|
//Строка подключения к БД
|
||||||
sConnectString: "DEMOP_CITKSERV_WAN",
|
sConnectString: "DEMOP_CITKSERV",
|
||||||
//Наименование модуля (для сессии БД)
|
//Наименование модуля (для сессии БД)
|
||||||
sSessionModuleName: "PARUS$ExchangeServer",
|
sSessionModuleName: "PARUS$ExchangeServer",
|
||||||
//Подключаемый модуль обслуживания БД (низкоуровневые функции работы с СУБД)
|
//Подключаемый модуль обслуживания БД (низкоуровневые функции работы с СУБД)
|
||||||
|
@ -17,8 +17,3 @@ exports.SERR_MODULES_BAD_INTERFACE = "ERR_MODULES_BAD_INTERFACE"; //Ошибоч
|
|||||||
|
|
||||||
//Типовые коды ошибок работы с объектами
|
//Типовые коды ошибок работы с объектами
|
||||||
exports.SERR_OBJECT_BAD_INTERFACE = "ERR_OBJECT_BAD_INTERFACE"; //Ошибочный интерфейс объекта
|
exports.SERR_OBJECT_BAD_INTERFACE = "ERR_OBJECT_BAD_INTERFACE"; //Ошибочный интерфейс объекта
|
||||||
|
|
||||||
//Типовые коды ошибок работы с БД
|
|
||||||
exports.SERR_DB_CONNECT = "ERR_DB_CONNECT"; //Ошибка подключения к БД
|
|
||||||
exports.SERR_DB_DISCONNECT = "ERR_DB_DISCONNECT"; //Ошибка отключения от БД
|
|
||||||
exports.SERR_DB_EXECUTE = "ERR_DB_EXECUTE"; //Ошибка исполнения функции в БД
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
//----------------------
|
//----------------------
|
||||||
|
|
||||||
const _ = require("lodash"); //Работа с массивами и объектами
|
const _ = require("lodash"); //Работа с массивами и объектами
|
||||||
|
const EventEmitter = require("events"); //Обработчик пользовательских событий
|
||||||
const glConst = require("../core/constants.js"); //Глобальные константы
|
const glConst = require("../core/constants.js"); //Глобальные константы
|
||||||
const { ServerError } = require("../core/server_errors.js"); //Типовая ошибка
|
const { ServerError } = require("../core/server_errors.js"); //Типовая ошибка
|
||||||
const { checkModuleInterface, makeModuleFullPath, checkObject } = require("../core/utils.js"); //Вспомогательные функции
|
const { checkModuleInterface, makeModuleFullPath, checkObject } = require("../core/utils.js"); //Вспомогательные функции
|
||||||
@ -21,13 +22,24 @@ const NLOG_STATE_INF = 0; //Информация
|
|||||||
const NLOG_STATE_WRN = 1; //Предупреждение
|
const NLOG_STATE_WRN = 1; //Предупреждение
|
||||||
const NLOG_STATE_ERR = 2; //Ошибка
|
const NLOG_STATE_ERR = 2; //Ошибка
|
||||||
|
|
||||||
|
//Типовые коды ошибок работы с БД
|
||||||
|
const SERR_DB_CONNECT = "ERR_DB_CONNECT"; //Ошибка подключения к БД
|
||||||
|
const SERR_DB_DISCONNECT = "ERR_DB_DISCONNECT"; //Ошибка отключения от БД
|
||||||
|
const SERR_DB_EXECUTE = "ERR_DB_EXECUTE"; //Ошибка исполнения функции в БД
|
||||||
|
|
||||||
|
//События модуля
|
||||||
|
const SEVT_DB_CONNECTOR_CONNECTED = "DB_CONNECTOR_CONNECTED"; //Подключено к БД
|
||||||
|
const SEVT_DB_CONNECTOR_DISCONNECTED = "DB_CONNECTOR_DISCONNECTED"; //Отключено от БД
|
||||||
|
|
||||||
//------------
|
//------------
|
||||||
// Тело модуля
|
// Тело модуля
|
||||||
//------------
|
//------------
|
||||||
|
|
||||||
class DBConnector {
|
class DBConnector extends EventEmitter {
|
||||||
//Конструктор
|
//Конструктор
|
||||||
constructor(prms) {
|
constructor(prms) {
|
||||||
|
//создадим экземпляр родительского класса
|
||||||
|
super();
|
||||||
//Проверяем структуру переданного объекта для подключения
|
//Проверяем структуру переданного объекта для подключения
|
||||||
let sCheckResult = checkObject(prms, {
|
let sCheckResult = checkObject(prms, {
|
||||||
fields: [
|
fields: [
|
||||||
@ -88,9 +100,10 @@ class DBConnector {
|
|||||||
try {
|
try {
|
||||||
this.connection = await this.connector.connect(this.connectSettings);
|
this.connection = await this.connector.connect(this.connectSettings);
|
||||||
this.bConnected = true;
|
this.bConnected = true;
|
||||||
|
this.emit(SEVT_DB_CONNECTOR_CONNECTED, this.connection);
|
||||||
return this.connection;
|
return this.connection;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_CONNECT, e.message);
|
throw new ServerError(SERR_DB_CONNECT, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Отключиться от БД
|
//Отключиться от БД
|
||||||
@ -100,9 +113,10 @@ class DBConnector {
|
|||||||
await this.connector.disconnect({ connection: this.connection });
|
await this.connector.disconnect({ connection: this.connection });
|
||||||
this.connection = {};
|
this.connection = {};
|
||||||
this.bConnected = false;
|
this.bConnected = false;
|
||||||
|
this.emit(SEVT_DB_CONNECTOR_DISCONNECTED, this.connection);
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_DISCONNECT, e.message);
|
throw new ServerError(SERR_DB_DISCONNECT, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,10 +140,10 @@ class DBConnector {
|
|||||||
let res = await Promise.all(srvsFuncs);
|
let res = await Promise.all(srvsFuncs);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, "Нет подключения к БД");
|
throw new ServerError(SERR_DB_EXECUTE, "Нет подключения к БД");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Запись в журнал работы
|
//Запись в журнал работы
|
||||||
@ -153,7 +167,7 @@ class DBConnector {
|
|||||||
let res = await this.connector.log(logData);
|
let res = await this.connector.log(logData);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ServerError(
|
throw new ServerError(
|
||||||
@ -162,7 +176,7 @@ class DBConnector {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, "Нет подключения к БД");
|
throw new ServerError(SERR_DB_EXECUTE, "Нет подключения к БД");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Запись информации в журнал работы
|
//Запись информации в журнал работы
|
||||||
@ -175,7 +189,7 @@ class DBConnector {
|
|||||||
let res = await this.putLog(logData);
|
let res = await this.putLog(logData);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Запись предупреждения в журнал работы
|
//Запись предупреждения в журнал работы
|
||||||
@ -188,7 +202,7 @@ class DBConnector {
|
|||||||
let res = await this.putLog(logData);
|
let res = await this.putLog(logData);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Запись ошибки в журнал работы
|
//Запись ошибки в журнал работы
|
||||||
@ -201,7 +215,7 @@ class DBConnector {
|
|||||||
let res = await this.putLog(logData);
|
let res = await this.putLog(logData);
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Считать очередную порцию исходящих сообщений
|
//Считать очередную порцию исходящих сообщений
|
||||||
@ -220,7 +234,7 @@ class DBConnector {
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, e.message);
|
throw new ServerError(SERR_DB_EXECUTE, e.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ServerError(
|
throw new ServerError(
|
||||||
@ -229,7 +243,7 @@ class DBConnector {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ServerError(glConst.SERR_DB_EXECUTE, "Нет подключения к БД");
|
throw new ServerError(SERR_DB_EXECUTE, "Нет подключения к БД");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,4 +255,9 @@ class DBConnector {
|
|||||||
exports.NLOG_STATE_INF = NLOG_STATE_INF;
|
exports.NLOG_STATE_INF = NLOG_STATE_INF;
|
||||||
exports.NLOG_STATE_WRN = NLOG_STATE_WRN;
|
exports.NLOG_STATE_WRN = NLOG_STATE_WRN;
|
||||||
exports.NLOG_STATE_ERR = NLOG_STATE_ERR;
|
exports.NLOG_STATE_ERR = NLOG_STATE_ERR;
|
||||||
|
exports.SERR_DB_CONNECT = SERR_DB_CONNECT;
|
||||||
|
exports.SERR_DB_DISCONNECT = SERR_DB_DISCONNECT;
|
||||||
|
exports.SERR_DB_EXECUTE = SERR_DB_EXECUTE;
|
||||||
|
exports.SEVT_DB_CONNECTOR_CONNECTED = SEVT_DB_CONNECTOR_CONNECTED;
|
||||||
|
exports.SEVT_DB_CONNECTOR_DISCONNECTED = SEVT_DB_CONNECTOR_DISCONNECTED;
|
||||||
exports.DBConnector = DBConnector;
|
exports.DBConnector = DBConnector;
|
||||||
|
@ -51,11 +51,14 @@ class Logger {
|
|||||||
this.bLogDB = true;
|
this.bLogDB = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Удаление объекта для протоколирования в БД
|
||||||
|
removeDBConnector() {
|
||||||
|
this.dbConnector = "";
|
||||||
|
this.bLogDB = false;
|
||||||
|
}
|
||||||
//Протоколирование в БД
|
//Протоколирование в БД
|
||||||
async logToDB(loggerMessage) {
|
async logToDB(loggerMessage) {
|
||||||
//Если надо протоколировать и есть чем
|
//Если надо протоколировать и есть чем
|
||||||
console.log(this.dbConnector.bConnected);
|
|
||||||
console.log("DB LOGGER IN");
|
|
||||||
if (this.bLogDB && this.dbConnector && this.dbConnector.bConnected) {
|
if (this.bLogDB && this.dbConnector && this.dbConnector.bConnected) {
|
||||||
//Если протоколируем стандартное сообщение
|
//Если протоколируем стандартное сообщение
|
||||||
if (loggerMessage instanceof LoggerMessage) {
|
if (loggerMessage instanceof LoggerMessage) {
|
||||||
@ -85,7 +88,6 @@ class Logger {
|
|||||||
await this.dbConnector.putLogInf(loggerMessage);
|
await this.dbConnector.putLogInf(loggerMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("DB LOGGER OUT");
|
|
||||||
}
|
}
|
||||||
//Протоколирование
|
//Протоколирование
|
||||||
async log(loggerMessage) {
|
async log(loggerMessage) {
|
||||||
|
110
core/out_queue.js
Normal file
110
core/out_queue.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Сервис интеграции ПП Парус 8 с WEB API
|
||||||
|
Модуль ядра: отработка очереди исходящих сообщений
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
// Подключение внешних библиотек
|
||||||
|
//------------------------------
|
||||||
|
|
||||||
|
const _ = require("lodash"); //Работа с массивами и коллекциями
|
||||||
|
const EventEmitter = require("events"); //Обработчик пользовательских событий
|
||||||
|
const { checkObject } = require("../core/utils.js"); //Вспомогательные функции
|
||||||
|
|
||||||
|
//--------------------------
|
||||||
|
// Глобальные идентификаторы
|
||||||
|
//--------------------------
|
||||||
|
|
||||||
|
//Типовые события
|
||||||
|
const SEVT_OUT_QUEUE_NEW = "OUT_QUEUE_NEW"; //Новое сообщение в очереди
|
||||||
|
|
||||||
|
//------------
|
||||||
|
// Тело модуля
|
||||||
|
//------------
|
||||||
|
|
||||||
|
//Класс очереди сообщений
|
||||||
|
class OutQueue extends EventEmitter {
|
||||||
|
//конструктор класса
|
||||||
|
constructor(prms, dbConn, logger) {
|
||||||
|
//Создадим экземпляр родительского класса
|
||||||
|
super();
|
||||||
|
//Проверяем структуру переданного объекта с параметрами очереди
|
||||||
|
let sCheckResult = checkObject(prms, {
|
||||||
|
fields: [{ sName: "nPortionSize", bRequired: true }, { sName: "nCheckTimeout", bRequired: true }]
|
||||||
|
});
|
||||||
|
//Если структура объекта в норме
|
||||||
|
if (!sCheckResult) {
|
||||||
|
//Хранилище очереди сообщений
|
||||||
|
this.queue = [];
|
||||||
|
//Признак функционирования обработчика
|
||||||
|
this.bWorking = false;
|
||||||
|
//Параметры очереди
|
||||||
|
_.extend(this, prms);
|
||||||
|
//Запомним подключение к БД
|
||||||
|
this.dbConn = dbConn;
|
||||||
|
//Запомним логгер
|
||||||
|
this.logger = logger;
|
||||||
|
} else {
|
||||||
|
throw new ServerError(
|
||||||
|
glConst.SERR_OBJECT_BAD_INTERFACE,
|
||||||
|
"Объект имеет недопустимый интерфейс: " + sCheckResult
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Добавление нового исходящего сообщения в очередь для отработки
|
||||||
|
addMessage(message) {
|
||||||
|
//Cоздадим новый элемент очереди
|
||||||
|
let tmp = {};
|
||||||
|
_.extend(tmp, message);
|
||||||
|
//добавим его в очередь
|
||||||
|
this.queue.push(tmp);
|
||||||
|
}
|
||||||
|
//Уведомление о получении нового сообщения
|
||||||
|
notifyNewOutMessage(message) {
|
||||||
|
//оповестим подписчиков о появлении нового отчета
|
||||||
|
this.emit(SEVT_OUT_QUEUE_NEW, message);
|
||||||
|
}
|
||||||
|
//Перезапуск опроса очереди исходящих сообщений
|
||||||
|
restartDetectingLoop() {
|
||||||
|
if (this.bWorking)
|
||||||
|
setTimeout(() => {
|
||||||
|
this.outDetectingLoop();
|
||||||
|
}, this.nCheckTimeout);
|
||||||
|
}
|
||||||
|
//Опрос очереди исходящих сообщений
|
||||||
|
async outDetectingLoop() {
|
||||||
|
//Сходим на сервер за очередным исходящим сообщением
|
||||||
|
try {
|
||||||
|
let outMsgs = await this.dbConn.getOutgoing({ nPortionSize: this.nPortionSize });
|
||||||
|
if (Array.isArray(outMsgs) && outMsgs.length > 0) {
|
||||||
|
await this.logger.info("Новое исходящее сообщение: " + outMsgs.toString());
|
||||||
|
} else {
|
||||||
|
await this.logger.info("Нет новых сообщений");
|
||||||
|
}
|
||||||
|
this.restartDetectingLoop();
|
||||||
|
} catch (e) {
|
||||||
|
await this.logger.error("При получении исходящего сообщения: " + e.sCode + ": " + e.sMessage);
|
||||||
|
this.restartDetectingLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Запуск обработки очереди печати
|
||||||
|
async startProcessing() {
|
||||||
|
await this.logger.info("Запуск обработчика очереди исходящих сообщений...");
|
||||||
|
this.bWorking = true;
|
||||||
|
this.outDetectingLoop();
|
||||||
|
await this.logger.info("Обработчик очереди исходящих сообщений запущен");
|
||||||
|
}
|
||||||
|
//Остановка обработки очереди печати
|
||||||
|
async stopProcessing() {
|
||||||
|
await this.logger.info("Останов обработчика очереди исходящих сообщений...");
|
||||||
|
this.bWorking = false;
|
||||||
|
await this.logger.info("Обработчик очереди исходящих сообщений остановлен");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------
|
||||||
|
// Интерфейс модуля
|
||||||
|
//-----------------
|
||||||
|
|
||||||
|
exports.SEVT_OUT_QUEUE_NEW = SEVT_OUT_QUEUE_NEW;
|
||||||
|
exports.OutQueue = OutQueue;
|
88
index.js
88
index.js
@ -2,3 +2,91 @@
|
|||||||
Сервис интеграции ПП Парус 8 с WEB API
|
Сервис интеграции ПП Парус 8 с WEB API
|
||||||
Точка входа в сервер приложений
|
Точка входа в сервер приложений
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//----------------------
|
||||||
|
// Подключение библиотек
|
||||||
|
//----------------------
|
||||||
|
|
||||||
|
require("module-alias/register");
|
||||||
|
const cfg = require("./config.js");
|
||||||
|
const lg = require("./core/logger.js");
|
||||||
|
const db = require("./core/db_connector.js");
|
||||||
|
const oq = require("./core/out_queue.js");
|
||||||
|
|
||||||
|
//--------------------------
|
||||||
|
// Глобальные идентификаторы
|
||||||
|
//--------------------------
|
||||||
|
|
||||||
|
let dbConn = new db.DBConnector(cfg.dbConnect); //Взаимодействие с БД
|
||||||
|
let logger = new lg.Logger(); //Протоколирование работы
|
||||||
|
let outQ = new oq.OutQueue(cfg.outgoing, dbConn, logger); //Отслеживание очереди исходящих
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
// Управление процессом сервера приложений
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
|
//При подключении к БД
|
||||||
|
const onDBConnected = async connection => {
|
||||||
|
logger.setDBConnector(dbConn);
|
||||||
|
await logger.info("Сервер приложений подключен к БД");
|
||||||
|
};
|
||||||
|
|
||||||
|
//При отключении от БД
|
||||||
|
const onDBDisconnected = async () => {
|
||||||
|
logger.removeDBConnector();
|
||||||
|
await logger.info("Сервер приложений отключен от БД");
|
||||||
|
};
|
||||||
|
|
||||||
|
//Запуск сервера
|
||||||
|
const run = async () => {
|
||||||
|
await logger.info("Запуск сервера приложений...");
|
||||||
|
dbConn.on(db.SEVT_DB_CONNECTOR_CONNECTED, onDBConnected);
|
||||||
|
dbConn.on(db.SEVT_DB_CONNECTOR_DISCONNECTED, onDBDisconnected);
|
||||||
|
await logger.info("Подключение сервера приложений к БД...");
|
||||||
|
try {
|
||||||
|
await dbConn.connect();
|
||||||
|
} catch (e) {
|
||||||
|
await logger.error("Ошибка подключения к БД: " + e.sCODE + ": " + e.sMessage);
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await outQ.startProcessing();
|
||||||
|
await logger.info("Сервер приложений запущен");
|
||||||
|
};
|
||||||
|
|
||||||
|
//Останов сервера
|
||||||
|
const stop = async () => {
|
||||||
|
await logger.warn("Останов сервера приложений...");
|
||||||
|
outQ.stopProcessing();
|
||||||
|
if (dbConn.bConnected) {
|
||||||
|
await logger.warn("Отключение сервера приложений от БД...");
|
||||||
|
try {
|
||||||
|
await dbConn.disconnect();
|
||||||
|
process.exit(0);
|
||||||
|
} catch (e) {
|
||||||
|
await logger.error("Ошибка отключения от БД: " + e.sCODE + ": " + e.sMessage);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Обработка события "выход" жизненного цикла процесса
|
||||||
|
process.on("exit", code => {
|
||||||
|
//Сообщим о завершении процесса
|
||||||
|
logger.warn("Сервер приложений остановлен (код: " + code + ") ");
|
||||||
|
});
|
||||||
|
|
||||||
|
//Перехват CTRL + C (останова процесса)
|
||||||
|
process.on("SIGINT", () => {
|
||||||
|
//Инициируем выход из процесса
|
||||||
|
stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
//------------
|
||||||
|
// Точка входа
|
||||||
|
//------------
|
||||||
|
|
||||||
|
//Старутем
|
||||||
|
run();
|
||||||
|
3
test.js
3
test.js
@ -25,9 +25,10 @@ const tests = async () => {
|
|||||||
try {
|
try {
|
||||||
//await l.warn("CONNECTING...");
|
//await l.warn("CONNECTING...");
|
||||||
await a.connect();
|
await a.connect();
|
||||||
|
for (i = 0; i <= 1000; i++) await l.info(i);
|
||||||
//await l.info("CONNECTED!");
|
//await l.info("CONNECTED!");
|
||||||
//await l.warn("READING SERVICES...");
|
//await l.warn("READING SERVICES...");
|
||||||
let srv = await a.getServices();
|
//let srv = await a.getServices();
|
||||||
//await l.info(srv);
|
//await l.info(srv);
|
||||||
console.log("1");
|
console.log("1");
|
||||||
await l.warn("DISCONNECTING...");
|
await l.warn("DISCONNECTING...");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user