2019-11-21 16:35:56 +00:00
|
|
|
import { createConnection, Connection } from "mysql";
|
|
|
|
import { conf } from "./ConfigHelper";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Database helper
|
|
|
|
*
|
|
|
|
* @author Pierre HUBERT
|
|
|
|
*/
|
|
|
|
|
2019-11-23 17:39:58 +00:00
|
|
|
export interface JoinTableInfo {
|
|
|
|
table: string,
|
|
|
|
condition: string
|
|
|
|
}
|
|
|
|
|
2019-11-22 08:31:13 +00:00
|
|
|
export interface QueryInformation {
|
|
|
|
table: string,
|
2019-11-23 17:39:58 +00:00
|
|
|
joins ?: Array<JoinTableInfo>,
|
2019-11-30 08:28:50 +00:00
|
|
|
fields ?: Array<string>,
|
2019-11-22 08:31:13 +00:00
|
|
|
where ?: Object,
|
2019-11-30 08:28:50 +00:00
|
|
|
customWhere ?: string,
|
|
|
|
customWhereArgs ?: Array<string>,
|
2019-11-23 17:17:20 +00:00
|
|
|
order ?: string,
|
2019-11-22 08:31:13 +00:00
|
|
|
limit ?: number,
|
|
|
|
}
|
|
|
|
|
2019-11-30 11:24:19 +00:00
|
|
|
export interface UpdateInformation {
|
|
|
|
table: string,
|
|
|
|
set: Object,
|
|
|
|
where ?: Object,
|
|
|
|
}
|
|
|
|
|
2019-11-23 18:31:23 +00:00
|
|
|
export interface CountQueryInformation {
|
|
|
|
table: string,
|
|
|
|
where ?: Object
|
|
|
|
}
|
|
|
|
|
2019-11-21 16:35:56 +00:00
|
|
|
export class DatabaseHelper {
|
|
|
|
|
|
|
|
private static connection : Connection;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect to database
|
|
|
|
*/
|
|
|
|
static async connect() {
|
|
|
|
this.connection = createConnection({
|
|
|
|
host: conf().database.host,
|
|
|
|
user: conf().database.user,
|
|
|
|
password: conf().database.password,
|
|
|
|
database: conf().database.dbName
|
|
|
|
});
|
|
|
|
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
this.connection.connect(err => {
|
|
|
|
|
|
|
|
if(err) {
|
|
|
|
console.error("Could not connect to database !");
|
|
|
|
console.error(err);
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.info("Connected to database.");
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-23 17:08:43 +00:00
|
|
|
/**
|
|
|
|
* Get an instance of the connection to the database
|
|
|
|
*/
|
|
|
|
static getConnection() : Connection {
|
|
|
|
return this.connection;
|
|
|
|
}
|
|
|
|
|
2019-11-22 08:31:13 +00:00
|
|
|
/**
|
|
|
|
* Query the database (SELECT)
|
|
|
|
*
|
|
|
|
* @param info Information about the query
|
|
|
|
*/
|
|
|
|
static async Query(info: QueryInformation) : Promise<Array<any>> {
|
|
|
|
// Prepare SQL request
|
2019-11-23 12:16:28 +00:00
|
|
|
let request = "SELECT ";
|
|
|
|
|
|
|
|
// Requested fields
|
|
|
|
request += info.fields ? info.fields.join(",") : "*";
|
|
|
|
|
|
|
|
request += " FROM " + info.table;
|
2019-11-23 17:39:58 +00:00
|
|
|
|
|
|
|
// Joins condition
|
|
|
|
if(info.joins) {
|
|
|
|
info.joins.forEach(join => {
|
|
|
|
request += " JOIN " + join.table + " ON " + join.condition
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-22 08:31:13 +00:00
|
|
|
let args = [];
|
|
|
|
|
|
|
|
// Add where arguments
|
|
|
|
if(info.where) {
|
|
|
|
request += " WHERE ";
|
|
|
|
|
|
|
|
for(const k in info.where) {
|
|
|
|
if(!info.where.hasOwnProperty(k))
|
|
|
|
continue;
|
2019-11-23 12:16:28 +00:00
|
|
|
const v = info.where[k].toString();
|
2019-11-22 08:31:13 +00:00
|
|
|
|
|
|
|
request += k;
|
|
|
|
request += v.startsWith("%") || v.endsWith("%") ? " LIKE " : " = "
|
|
|
|
request += "? AND "
|
|
|
|
|
|
|
|
args.push(v);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Remove the last (useless) AND
|
|
|
|
request = request.substr(0, request.length - 4)
|
|
|
|
}
|
|
|
|
|
2019-11-30 08:28:50 +00:00
|
|
|
// Add custom WHERE clause
|
|
|
|
if(info.customWhere) {
|
|
|
|
|
|
|
|
if(!info.where)
|
|
|
|
request += " WHERE " + info.customWhere + " ";
|
|
|
|
else
|
|
|
|
request += " AND (" + info.customWhere + ")";
|
|
|
|
|
|
|
|
if(info.customWhereArgs)
|
|
|
|
info.customWhereArgs.forEach((e) => args.push(e));
|
|
|
|
}
|
|
|
|
|
2019-11-23 17:17:20 +00:00
|
|
|
// Order (if any)
|
|
|
|
if(info.order)
|
|
|
|
request += " ORDER BY " + info.order + " ";
|
|
|
|
|
2019-11-22 08:31:13 +00:00
|
|
|
// Limit (if any)
|
|
|
|
if(info.limit)
|
|
|
|
request += " LIMIT " + info.limit;
|
|
|
|
|
|
|
|
|
|
|
|
// Execute request
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
|
|
this.connection.query( request, args, (err, result, fields) => {
|
|
|
|
if(err) {
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(result);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query a single row on the database
|
|
|
|
*
|
|
|
|
* @param info Information about the request
|
|
|
|
* @returns First matching row / null if none found
|
|
|
|
*/
|
|
|
|
static async QueryRow(info : QueryInformation) : Promise<any | null> {
|
|
|
|
info.limit = 1;
|
|
|
|
const result = await this.Query(info);
|
|
|
|
|
|
|
|
if(result.length == 0)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return result[0];
|
|
|
|
}
|
2019-11-23 12:16:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert an new entry into the database
|
|
|
|
*
|
|
|
|
* @param info Information about the entry
|
|
|
|
* @returns The ID of the inserted column (if any)
|
|
|
|
*/
|
|
|
|
static async InsertRow(table : string, values : any) : Promise<number> {
|
|
|
|
return new Promise((resolve, reject) => {
|
2019-11-23 13:03:14 +00:00
|
|
|
this.connection.query("INSERT INTO " + table + " SET ?", values, (err, results, f) => {
|
2019-11-23 12:16:28 +00:00
|
|
|
if(err)
|
|
|
|
reject(err);
|
2019-11-23 13:03:14 +00:00
|
|
|
else
|
|
|
|
resolve(results.insertId);
|
2019-11-23 12:16:28 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2019-11-23 13:03:14 +00:00
|
|
|
|
2019-11-30 11:24:19 +00:00
|
|
|
/**
|
|
|
|
* Perform update on the database
|
|
|
|
*
|
|
|
|
* @param info Information about the request
|
|
|
|
* @returns The number of affected rows
|
|
|
|
*/
|
|
|
|
static async UpdateRows(info : UpdateInformation) : Promise<number> {
|
|
|
|
let sql = "UPDATE " + info.table + " SET ";
|
|
|
|
let args = [];
|
|
|
|
|
|
|
|
// Process updates
|
|
|
|
let isFirst = true;
|
|
|
|
for (const key in info.set) {
|
|
|
|
if (info.set.hasOwnProperty(key)) {
|
|
|
|
const value = info.set[key];
|
|
|
|
|
|
|
|
if(!isFirst)
|
|
|
|
sql += ", ";
|
|
|
|
else
|
|
|
|
isFirst = false;
|
|
|
|
|
|
|
|
sql += key + " = ? "
|
|
|
|
args.push(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process conditions
|
|
|
|
isFirst = true;
|
|
|
|
if(info.where) {
|
|
|
|
sql += " WHERE ";
|
|
|
|
for (const key in info.where) {
|
|
|
|
if (info.where.hasOwnProperty(key)) {
|
|
|
|
const value = info.where[key];
|
|
|
|
|
|
|
|
if(!isFirst)
|
|
|
|
sql += " AND ";
|
|
|
|
else
|
|
|
|
isFirst = false;
|
|
|
|
|
|
|
|
sql += key + " = ? "
|
|
|
|
args.push(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw Error("Error : Updates without conditions are blocked for security!");
|
|
|
|
|
|
|
|
// Execute request
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
|
|
this.connection.query(sql, args, (err, results, f) => {
|
|
|
|
if(err){
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(results.affectedRows);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-11-23 13:03:14 +00:00
|
|
|
/**
|
|
|
|
* Delete entries from a table
|
|
|
|
*
|
|
|
|
* @param table Target table
|
|
|
|
* @param where Where arguments
|
|
|
|
*/
|
|
|
|
static async DeleteRows(table: string, where: any) {
|
|
|
|
|
|
|
|
let whereArgs = "";
|
|
|
|
let args = [];
|
|
|
|
|
|
|
|
// Process conditions
|
|
|
|
for (const key in where) {
|
|
|
|
if (where.hasOwnProperty(key)) {
|
|
|
|
const value = where[key];
|
|
|
|
whereArgs += (whereArgs == "" ? "" : " AND ") + key + " = ?";
|
|
|
|
args.push(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(whereArgs == "")
|
|
|
|
throw Error("Error : table could accidentally get purged!");
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.connection.query("DELETE FROM " + table + " WHERE " + whereArgs, args, (err, r, f) => {
|
|
|
|
if(err) reject(err)
|
|
|
|
else resolve();
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
2019-11-23 18:31:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform a COUNT query on the database
|
|
|
|
*
|
|
|
|
* @param info Information about the count query
|
|
|
|
*/
|
|
|
|
static async Count(info: CountQueryInformation) : Promise<number> {
|
|
|
|
|
|
|
|
let sql = "SELECT COUNT(*) as count FROM " + info.table;
|
|
|
|
let args = [];
|
|
|
|
|
|
|
|
if(info.where) {
|
|
|
|
sql += " WHERE ";
|
|
|
|
|
|
|
|
for (const key in info.where) {
|
|
|
|
if (info.where.hasOwnProperty(key)) {
|
|
|
|
const value = info.where[key];
|
|
|
|
sql += "AND " + key + " = ? ";
|
|
|
|
args.push(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sql = sql.replace("WHERE AND", "WHERE");
|
|
|
|
}
|
|
|
|
|
|
|
|
return await new Promise((r, e) => {
|
|
|
|
this.connection.query(sql, args, (err, results, f) => {
|
|
|
|
if(err){
|
|
|
|
e(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
r(results[0].count);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
2019-11-21 16:35:56 +00:00
|
|
|
}
|