umbot
Version:
Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф
378 lines (377 loc) • 15 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MongoAdapter = void 0;
const Base_1 = require("../Base/Base");
const index_1 = require("../../../index");
const mongodb_1 = require("mongodb");
/**
* Адаптер для работы с базой данных mongodb
*/
class MongoAdapter extends Base_1.Base {
/**
* Формат базы данных
*/
dbFormat = 'mongodb';
constructor(options) {
super(options);
}
/**
* Метод инициализации плагина.
* Вызывается один раз при подключении через `bot.use()`.
* @param appContext Контекст приложения
*/
init(appContext) {
if (this._dbOptions) {
appContext.appConfig.db ??= { host: '', user: '', pass: '', database: '' };
appContext.appConfig.db.options =
this._dbOptions.options || appContext.appConfig.db?.options;
appContext.appConfig.db.host = this._dbOptions.host || appContext.appConfig.db?.host;
appContext.appConfig.db.user = this._dbOptions.user || appContext.appConfig.db?.user;
appContext.appConfig.db.pass = this._dbOptions.pass || appContext.appConfig.db?.pass;
appContext.appConfig.db.database =
this._dbOptions.database || appContext.appConfig.db?.database;
}
super.init(appContext);
}
async connect() {
const errors = [];
let mongoClient = null;
let mongoConnect = null;
if (this._appContext.appConfig.db) {
try {
const options = {
timeoutMS: 3000,
serverSelectionTimeoutMS: 2000, // Тайм-аут на выбор сервера
connectTimeoutMS: 2000,
socketTimeoutMS: 2000,
maxPoolSize: 50,
...this._appContext.appConfig.db.options,
serverApi: {
version: mongodb_1.ServerApiVersion.v1,
strict: true,
deprecationErrors: true,
...this._appContext.appConfig.db.options?.serverApi,
},
};
if (this._appContext.appConfig.db.user) {
options.auth = {
username: this._appContext.appConfig.db.user,
password: this._appContext.appConfig.db.pass,
};
}
mongoClient = new mongodb_1.MongoClient(this._appContext.appConfig.db.host, options);
const connect = async () => {
if (!mongoClient) {
return false;
}
// Проверяем, есть ли активное соединение перед закрытием
if (this._appContext.database.databaseInfo?.mongoClient) {
await mongoClient.close(true);
}
mongoConnect = await mongoClient.connect();
// Проверяем подключение сразу после установки
return await this.isConnected();
};
if (!(await connect())) {
await new Promise((resolve) => {
setTimeout(resolve, 2000).unref();
});
if (!(await connect())) {
throw new Error('Failed to verify database connection');
}
}
return true;
}
catch (err) {
errors.push(err.message);
// mongoConnect = null;
mongoClient = null;
this._saveLog('При подключении в базе данных произошла ошибка:', err);
return false;
}
}
else {
errors.push('Отсутствуют данные для подключения!');
}
if (this._appContext.database.databaseInfo) {
this._appContext.database.databaseInfo.mongoClient = mongoClient;
this._appContext.database.databaseInfo.mongoConnect = mongoConnect;
}
else {
this._appContext.database.databaseInfo = {
mongoClient,
mongoConnect,
};
}
if (errors.length > 0) {
this._saveLog(`При подключении в базе данных произошли следующие ошибки: ${JSON.stringify(errors)}`);
return false;
}
return true;
}
/**
* Выполняет UPDATE-запрос.
* @param updateData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async _update(updateData) {
let update = updateData.data;
let select = updateData.query;
if (this._appContext.database.databaseInfo) {
update = this.validate(updateData, update);
select = this.validate(updateData, select);
if (updateData.primaryKeyName) {
return !!(await this.query(async (_client, db) => {
try {
const collection = db.collection(updateData.tableName);
const result = await collection.updateOne(select, {
$set: update,
});
return {
status: true,
data: { modifiedCount: result.modifiedCount },
};
}
catch (err) {
return {
status: false,
error: err,
};
}
}));
}
}
return false;
}
/**
* Выполняет INSERT-запрос.
* @param insertData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async _insert(insertData) {
let insert = insertData.data;
if (this._appContext.database.databaseInfo) {
insert = this.validate(insertData, insert);
if (insertData.primaryKeyName) {
return !!(await this.query(async (_client, db) => {
try {
const collection = db.collection(insertData.tableName);
const result = await collection.insertOne(insert);
return {
status: true,
data: { insertedId: result.insertedId },
};
}
catch (err) {
return {
status: false,
error: err instanceof Error ? err : 'Unknown error',
};
}
}));
}
}
return false;
}
/**
* Выполняет DELETE-запрос.
* @param removeData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async _remove(removeData) {
let remove = removeData.query;
if (this._appContext.database.databaseInfo) {
remove = this.validate(removeData, remove);
return !!(await this.query(async (_client, db) => {
try {
const collection = db.collection(removeData.tableName);
const result = await collection.deleteOne(remove);
return {
status: true,
data: { deletedCount: result.deletedCount },
};
}
catch (err) {
return {
status: false,
error: err instanceof Error ? err : 'Unknown error',
};
}
}));
}
return false;
}
/**
* Выполняет произвольный запрос через callback.
*
* @param callback функция обработчик
*/
async _query(callback) {
const vDB = this._appContext.database.databaseInfo;
try {
if (vDB?.mongoConnect) {
const client = vDB.mongoConnect;
const db = client.db(this._appContext.appConfig.db?.database);
const data = await callback(client, db);
if (data.status) {
return data.data;
}
this._saveLog(data.error + '');
return null;
}
else {
this._saveLog('Не удалось выполнить запрос.');
return null;
}
}
catch (err) {
this._saveLog(err, err);
return null;
}
}
/**
* Выполняет произвольный запрос через callback.
*
* Внутри себя вызывает this._query, основное отличие в том, что в данном методе пишутся метрики.
* @param callback функция обработчик
*/
async query(callback) {
return this._query(callback);
}
/**
* Валидация запросов.
* Валидирует запрос, приводя его к корректному виду
* @param query
* @param element
*/
validate(query, element) {
if (!element) {
return {};
}
const rules = query.rules;
if (rules) {
rules.forEach((rule) => {
let type = 'number';
switch (rule.type) {
case 'string':
case 'text':
type = 'string';
break;
case 'int':
case 'integer':
case 'bool':
type = 'number';
break;
}
rule.name.forEach((data) => {
if (type === 'string') {
if (rule.max !== undefined) {
element[data] = index_1.Text.resize(element[data], rule.max);
}
element[data] = this.escapeString(element[data]);
}
else {
element[data] = +element[data];
}
});
});
}
return element;
}
/**
* Выполняет SELECT-запрос.
* @param selectData Дополнительная информация для запроса. Содержит информацию о таблице и структуре.
* @param where Сам запрос
* @param isOne Определяет нужно ли вернуть только 1 найденную запись, либо отдать все доступные данные.
*/
async _select(selectData, where, isOne = false) {
if (this._appContext.database.databaseInfo) {
return (await this.query(async (_client, db) => {
try {
const collection = db.collection(selectData.tableName);
let results;
if (isOne) {
results = await collection.findOne(where || {});
}
else {
results = await collection
.find(where || {})
.toArray();
}
return {
status: !!results,
data: results,
};
}
catch (err) {
return {
status: false,
error: err,
};
}
}));
}
return {
status: false,
error: 'Не удалось получить данные',
};
}
/**
* Проверяет, установлено ли соединение с БД.
*/
async isConnected() {
if (!this._appContext.database.databaseInfo) {
return false;
}
try {
if (!this._appContext.database.databaseInfo.mongoClient) {
return false;
}
// Пингуем базу данных для проверки подключения
await this._appContext.database.databaseInfo.mongoClient.db().command({ ping: 1 });
return true;
}
catch (err) {
this._appContext.logError(err.message, {
error: err,
});
return false;
}
}
/**
* Сохранение логов
* @param errorMsg
* @param error
* @protected
*/
_saveLog(errorMsg, error) {
this._appContext?.logError(`MongoDB: ${errorMsg}`, {
error,
});
}
/**
* Закрывает все подключения к БД.
* Все процессы завершаются, и происходит сохранение данных.
*/
async destroy() {
return this.close('');
}
/**
* Закрывает подключение к определенной таблице.
* В MongoDB нет концепции "закрытия таблицы". Метод close() закрывает всё соединение с базой.
* @param tableName
*/
async close(tableName) {
await super.close(tableName);
if (this._appContext.database.databaseInfo) {
try {
await this._appContext.database.databaseInfo.mongoClient.close();
this._appContext.database.databaseInfo.mongoConnect = null;
this._appContext.database.databaseInfo.mongoClient = null;
}
catch (err) {
this._appContext.logError(err.message, {
error: err,
});
}
}
}
}
exports.MongoAdapter = MongoAdapter;