botserver/deploy/core.gbapp/services/GBCoreService.ts

319 lines
11 KiB
TypeScript
Raw Normal View History

2018-04-21 02:59:30 -03:00
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of Pragmatismo.io. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
"use strict";
const logger = require("../../../src/logger");
import { Sequelize } from "sequelize-typescript";
2018-04-21 02:59:30 -03:00
import { GBConfigService } from "./GBConfigService";
import { IGBInstance, IGBCoreService } from "botlib";
2018-04-21 02:59:30 -03:00
import { GuaribasInstance } from "../models/GBModel";
/**
* Core service layer.
*/
export class GBCoreService implements IGBCoreService {
2018-09-09 14:39:37 -03:00
/**
* Data access layer instance.
*/
2018-04-21 02:59:30 -03:00
public sequelize: Sequelize;
2018-09-09 14:39:37 -03:00
/**
* Allows filtering on SQL generated before send to the database.
*/
private queryGenerator: any;
2018-09-09 14:39:37 -03:00
/**
* Custom create table query.
*/
private createTableQuery: (tableName, attributes, options) => string;
2018-09-09 14:39:37 -03:00
/**
* Custom change column query.
*/
private changeColumnQuery: (tableName, attributes) => string;
2018-04-21 02:59:30 -03:00
/**
* Dialect used. Tested: mssql and sqlite.
2018-09-09 14:39:37 -03:00
*/
private dialect: string;
2018-04-21 02:59:30 -03:00
2018-09-09 14:39:37 -03:00
/**
* Constructor retrieves default values.
*/
2018-04-21 02:59:30 -03:00
constructor() {
this.dialect = GBConfigService.get("DATABASE_DIALECT");
}
/**
* Gets database config and connect to storage.
2018-09-09 14:39:37 -03:00
*/
async initDatabase() {
return new Promise((resolve, reject) => {
try {
let host: string | undefined;
let database: string | undefined;
let username: string | undefined;
let password: string | undefined;
let storage: string | undefined;
2018-04-21 02:59:30 -03:00
if (this.dialect === "mssql") {
host = GBConfigService.get("DATABASE_HOST");
database = GBConfigService.get("DATABASE_NAME");
username = GBConfigService.get("DATABASE_USERNAME");
password = GBConfigService.get("DATABASE_PASSWORD");
} else if (this.dialect === "sqlite") {
storage = GBConfigService.get("DATABASE_STORAGE");
}
let logging =
GBConfigService.get("DATABASE_LOGGING") === "true"
? (str: string) => {
logger.info(str);
}
2018-09-09 14:39:37 -03:00
: false;
let encrypt = GBConfigService.get("DATABASE_ENCRYPT") === "true";
this.sequelize = new Sequelize({
host: host,
database: database,
username: username,
password: password,
logging: logging,
operatorsAliases: false,
dialect: this.dialect,
storage: storage,
dialectOptions: {
encrypt: encrypt
},
pool: {
max: 32,
min: 8,
idle: 40000,
evict: 40000,
acquire: 40000
2018-09-09 14:39:37 -03:00
}
});
if (this.dialect === "mssql") {
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
this.createTableQuery = this.queryGenerator.createTableQuery;
this.queryGenerator.createTableQuery = (
tableName,
attributes,
options
) => this.createTableQueryOverride(tableName, attributes, options);
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
this.changeColumnQueryOverride(tableName, attributes);
2018-09-09 14:39:37 -03:00
}
resolve();
} catch (error) {
reject(error);
}
});
}
/**
* SQL:
*
* // let sql: string = '' +
* // 'IF OBJECT_ID(\'[UserGroup]\', \'U\') IS NULL\n' +
* // 'CREATE TABLE [UserGroup] (\n' +
* // ' [id] INTEGER NOT NULL IDENTITY(1,1),\n' +
* // ' [userId] INTEGER NULL,\n' +
* // ' [groupId] INTEGER NULL,\n' +
* // ' [instanceId] INTEGER NULL,\n' +
* // ' PRIMARY KEY ([id1], [id2]),\n' +
* // ' FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' +
* // ' FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId1]) ON DELETE NO ACTION,\n' +
* // ' FOREIGN KEY ([instanceId]) REFERENCES [Instance] ([instanceId]) ON DELETE NO ACTION);';
*/
private createTableQueryOverride(tableName, attributes, options): string {
let sql: string = this.createTableQuery.apply(this.queryGenerator, [
tableName,
attributes,
options
]);
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches) {
const table = matches[1];
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
sql = sql.replace(
re2,
(match: string, ...args: any[]): string => {
return "CONSTRAINT [" + table + "_pk] " + match;
}
);
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g;
sql = sql.replace(
re3,
(match: string, ...args: any[]): string => {
const fkcols = args[0];
let fkname = table;
let matches = re4.exec(fkcols);
while (matches != null) {
fkname += "_" + matches[1];
matches = re4.exec(fkcols);
}
return "CONSTRAINT [" + fkname + "_fk] FOREIGN KEY (" + fkcols + ")";
}
);
}
return sql;
}
/**
* SQL:
* let sql = '' +
* 'ALTER TABLE [UserGroup]\n' +
* ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,\n' +
* ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, \n' +
* ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION;\n';
*/
private changeColumnQueryOverride(tableName, attributes): string {
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [
tableName,
attributes
]);
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches) {
const table = matches[1];
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re3 = /\[([^\]]*)\]/g;
sql = sql.replace(
re2,
(match: string, ...args: any[]): string => {
const fkcols = args[2];
let fkname = table;
let matches = re3.exec(fkcols);
while (matches != null) {
fkname += "_" + matches[1];
matches = re3.exec(fkcols);
}
return (
(args[0] ? args[0] : "") +
"CONSTRAINT [" +
fkname +
"_fk] FOREIGN KEY (" +
fkcols +
")"
);
}
);
}
return sql;
2018-04-21 02:59:30 -03:00
}
2018-09-09 14:39:37 -03:00
async syncDatabaseStructure() {
return new Promise((resolve, reject) => {
if (GBConfigService.get("DATABASE_SYNC") === "true") {
const alter = GBConfigService.get("DATABASE_SYNC_ALTER") === "true";
const force = GBConfigService.get("DATABASE_SYNC_FORCE") === "true";
logger.info("Syncing database...");
this.sequelize
.sync({
2018-09-09 14:39:37 -03:00
alter: alter,
force: force
})
.then(
value => {
logger.info("Database synced.");
resolve(value);
},
err => reject(err)
);
} else {
logger.info("Database synchronization is disabled.");
resolve();
}
});
2018-04-21 02:59:30 -03:00
}
/**
* Loads all items to start several listeners.
*/
2018-09-09 14:39:37 -03:00
async loadInstances(): Promise<IGBInstance> {
return new Promise((resolve, reject) => {
GuaribasInstance.findAll({})
.then((items: IGBInstance[]) => {
if (!items) items = [];
2018-04-21 02:59:30 -03:00
if (items.length == 0) {
resolve([]);
} else {
resolve(items);
}
})
.catch(reason => {
if (reason.message.indexOf("no such table: GuaribasInstance") != -1) {
resolve([]);
} else {
logger.info(`GuaribasServiceError: ${reason}`);
reject(reason);
}
});
});
2018-04-21 02:59:30 -03:00
}
/**
* Loads just one Bot instance.
*/
2018-09-09 14:39:37 -03:00
async loadInstance(botId: string): Promise<IGBInstance> {
return new Promise<IGBInstance>((resolve, reject) => {
let options = { where: {} };
2018-04-21 02:59:30 -03:00
if (botId != "[default]") {
options.where = { botId: botId };
}
2018-04-21 02:59:30 -03:00
GuaribasInstance.findOne(options)
.then((instance: IGBInstance) => {
if (instance) {
resolve(instance);
} else {
resolve(null);
}
})
.catch(err => {
logger.info(`GuaribasServiceError: ${err}`);
reject(err);
});
});
2018-04-21 02:59:30 -03:00
}
}