vitis-engine-lib
Version:
Lib used on Vitis engines
262 lines (226 loc) • 7.44 kB
text/typescript
import {
Client,
Connection
} from 'pg';
import Logger from './logger';
import fs from 'fs';
/**
* Classe Db
*/
export default class Db {
// Properties
private properties_: any;
// Paramètres de connexion
private connexionParams_: any;
// Client base de données
private client_: any;
// Définit si la connexion est en cours
private connected_: boolean = false;
// Définit si l'on doit tenter de se connecter en ssl si possible
private preferMode_: boolean = false;
// Timeout avant déconnexion
private connectionTimeout_: number = 10000;
// Date de la dernière requête
private lastQueryDate_: number = 0;
// fonction timeout de déconnexion
private disconnectFncTimeout_: any;
// Logger
private logger_: Logger;
/**
* Constructeur
*/
constructor(properties: any, connexionParams: any, logger: Logger) {
this.properties_ = properties;
this.connexionParams_ = connexionParams;
this.logger_ = logger;
if (this.connexionParams_.autoSslConfig === true) {
this.connexionParams_.ssl = this.loadSslConfigFromProperties();
}
}
/**
*
* @returns {boolean}
*/
public isConnected(): boolean {
return this.connected_;
}
/**
* Connexion à la base
*/
public connect(): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.client_ = new Client(this.connexionParams_);
this.client_.connect().then(() => {
this.logger_.log('Connected to the database', null, 'DEBUG');
this.connected_ = true;
}).catch((err: any) => {
this.connected_ = false;
this.logger_.log('DB ERROR : Cannot connect to the database', err, 'ERROR');
}).finally(() => {
resolve();
})
});
}
/**
* Connexion à la base
*/
public connectWithSslCheck() {
return new Promise<void>((resolve, reject) => {
const self = this;
const tmp_con_: any = new Connection();
tmp_con_.stream.once('data', function (buffer: any) {
const responseCode = buffer.toString('utf8');
switch (responseCode) {
case 'S': // Server supports SSL connections, continue with a secure connection
break;
case 'N': // Server does not support SSL connections
// si l'appli est en mode prefer et que le SSL n'est pas actif
// on désactive le ssl
if (self.preferMode_ && self.connexionParams_.ssl !== false) {
self.logger_.log('SSL is disabled for this database', null, 'DEBUG');
delete self.connexionParams_.autoSslConfig;
delete self.connexionParams_.ssl;
}
break;
default: // Any other response byte, including 'E' (ErrorResponse) indicating a server error
self.logger_.log('DB ERROR : Cannot get SSL informations', null, 'ERROR');
return;
}
tmp_con_.end()
tmp_con_.stream.destroy()
self.connect().then(() => {
resolve();
});
});
tmp_con_.stream.on('error', function (error: Error) {
self.logger_.log('DB ERROR : Cannot get SSL informations', error, 'ERROR');
reject();
});
tmp_con_.stream.once('ready', function () {
setTimeout(() => {
tmp_con_.requestSsl();
}, 0)
});
tmp_con_.stream.connect(this.connexionParams_.port, this.connexionParams_.host)
});
}
/**
* Surcharge la configuration SSL pour la connection postgres
*/
private loadSslConfigFromProperties() {
// require, prefer et allow ne nécéssite pas la vérification des certificats
const oOptions: any = { rejectUnauthorized: false };
// on active le mode pour se reconnecter en non ssl en cas d'echec
if (['prefer', 'allow'].indexOf(this.properties_.db_ssl_mode) > -1) {
this.preferMode_ = true;
return oOptions;
}
if (this.properties_.db_ssl_mode === 'require') {
return oOptions;
}
if (['verify-ca', 'verify-full'].indexOf(this.properties_.db_ssl_mode) > -1) {
// active la vérification des certificats serveurs
oOptions.rejectUnauthorized = true;
// Cert Authority
if (fs.existsSync(this.properties_.db_ssl_root_cert)) {
oOptions.ca = fs.readFileSync(this.properties_.db_ssl_root_cert).toString();
}
// Key
if (fs.existsSync(this.properties_.db_ssl_key)) {
oOptions.key = fs.readFileSync(this.properties_.db_ssl_key).toString();
}
// Certificat
if (fs.existsSync(this.properties_.db_ssl_cert)) {
oOptions.cert = fs.readFileSync(this.properties_.db_ssl_cert).toString();
}
return oOptions;
}
// si le ssl n'est pas requis on le désactive
// disabled
return false;
}
/**
* Change la durée maximale d'une connexion à la base
* @param iDuration Durée en millisecond
*/
public setMaxConnectionTime(iDuration: number) {
if (iDuration > 0) {
this.connectionTimeout_ = iDuration;
} else {
this.logger_.log('DB ERROR : you are trying to set a max connection time negative or null (value unchanged)', null, 'ERROR');
}
}
/**
* Désactive la déconnexion automatique à la base
*/
public disableTimeOut() {
this.connectionTimeout_ = 0;
}
/**
* Déconnexion de la base
*/
public disconnect() {
this.client_.end();
this.connected_ = false;
if (this.disconnectFncTimeout_) {
clearTimeout(this.disconnectFncTimeout_);
}
}
/**
* Lance une requette
* @param {string} sQuery Requette, pour utiliser des paramêtres utiliser $1, $2...
* @param {Array | undefined} aParams Paramètres sous forme de tableau
*/
private execQuery_(sQuery: string, aParams: any[] = []) {
return new Promise((resolve, reject) => {
// Date de dernière requête
this.lastQueryDate_ = Date.now();
// Lancement de la requête
this.client_.query(sQuery, aParams, (err: any, res: any) => {
// Retour
if (err) {
this.logger_.log('DB ERROR : ', err.stack);
reject(err.stack);
} else {
resolve(res.rows);
}
// Déconnexion de la base après 10 secondes d'innactivité
if (this.disconnectFncTimeout_) {
clearTimeout(this.disconnectFncTimeout_);
}
if (this.connectionTimeout_ > 0) {
this.disconnectFncTimeout_ = setTimeout(() => {
if (Date.now() > (this.lastQueryDate_ - this.connectionTimeout_)) {
if (this.connected_) {
this.disconnect();
}
}
}, this.connectionTimeout_ + 10);
}
});
});
}
public query(sQuery: string, aParams: any[] = [], bAutoConnect = true) {
// si la connection est déjà faite
if (this.connected_ && this.client_) {
return this.execQuery_(sQuery, aParams);
}
if (!this.connected_ && bAutoConnect) {
return new Promise((resolve, reject) => {
// Connexion à la base
this.connectWithSslCheck().then(() => {
this.execQuery_(sQuery, aParams).then((rows) => {
resolve(rows);
}).catch((err) => {
reject(err);
});
}).catch((err) => {
reject(err);
})
});
}
return new Promise((resolve, reject) => {
reject();
})
}
}