UNPKG

backstrap-server

Version:

Backstrap is a lightweight framework for building APIs

682 lines (615 loc) 19.9 kB
// =============================================================================== // UTILITY FUNCTIONS // =============================================================================== const util = require('util'); const path = require('path'); const rootDir = path.dirname(require.main.filename); const fs = require('fs'); var nodemailer = require('nodemailer'); var smtpTransport = require('nodemailer-smtp-transport'); const nodemailerSendgrid = require('nodemailer-sendgrid'); const crypto = require('crypto'); var UtilitiesExtension; try { UtilitiesExtension = require(`${rootDir}/utilities_ext.js`); } catch(e) { console.error('INITIALIZATION ERROR -- utilities_ext.js'); throw(e); } class Utilities { constructor() { this.dataAccess = null; this.settings = null; this.eventLog = null; this.errorLog = null; this.sessionLog = null; this.extension = {}; this.mailTransport = null; } init(s) { this.settings = s; let mailAuth = {}; let mo = this.settings.mail_options; if(mo) { if(mo.user) mailAuth.user = mo.user; if(mo.pass) mailAuth.pass = mo.pass; if(mo.api_key) mailAuth.api_key = mo.api_key; var options = {}; if (mo.service){ // SEND GRID WANTS ONLY THE API KEY IN THE AUTH FIELD IF AVAILABLE if(mo.service.toLowerCase() === 'sendgrid' && mailAuth.api_key) {if([]) this.mailTransport = nodemailer.createTransport(nodemailerSendgrid({apiKey:mailAuth.api_key})); } else { options = { service: mo.service, auth: mailAuth } if(mo.port) options.port = mo.port; if(mo.tls) options.tls = mo.tls this.mailTransport = nodemailer.createTransport(smtpTransport(options)); } } else { options = { host: mo.host, port: mo.port, auth: mailAuth } if(mo.tls) options.tls = mo.tls; this.mailTransport = nodemailer.createTransport(smtpTransport(options)); } } // IF THERE IS A UTILITIES DIRECTORY SPECIFIED IN Settings.json // RUN THROUGH IT AND INSTANTIATE EACH SERVICE FILE let utilsDir = this.settings.utilities_directory; if(utilsDir != null) { utilsDir.replace(/^\.\//, ''); utilsDir.replace(/^\//, ''); let utils = fs.readdirSync(utilsDir); utils.forEach((utilFile) => { // DON'T OVERWRITE utilities.extension if(utilFile.toLowerCase() !== 'extension') { let fileNoExt = utilFile.replace('.js', ''); try { let Util = require(`${rootDir}/${utilsDir}/${utilFile}`); this[fileNoExt] = new Util(this); } catch(e) { throw e; } } }); } } async getHash(alg, data, length) { if(alg == null) alg = 'sha256'; var h = crypto.createHash(alg); let byteCount = length || 10; if(data == null) data = crypto.randomBytes(byteCount); h.update(data); var digest = h.digest('hex'); if(length != null) digest = digest.substring(0, length); return Promise.resolve(digest); } setDataAccess(da) { this.dataAccess = da; if(this.extension !== undefined && this.extension !== null) { this.extension = new UtilitiesExtension(this); } } setLogs(evl, erl, sesl) { this.eventLog = evl; this.errorLog = erl; this.sessionLog = sesl; } async validateUsername(newUsername, existingUsername) { return new Promise((resolve, reject) => { if (newUsername === existingUsername) { resolve(); } else { this.dataAccess.getUserByUserName(newUsername) .then((userFound) => { var errorObj = new ErrorObj(400, 'u0053', __filename, 'validateUsername', 'a user already exists with the username provided' ); reject(errorObj); }) .catch((err) => { resolve(); }); } }); } async validateEmail(newEmail, existingEmail) { return new Promise((resolve, reject) => { if (newEmail === existingEmail) { resolve(); } else { this.dataAccess.getUserByEmail(newEmail) .then((userFound) => { var errorObj = new ErrorObj(400, 'u0054', __filename, 'bsuser', 'a bsuser already exists with the email provided' ); reject(errorObj); }) .catch((err) => { if (err.err_code == 'da2001') { // THERE WERE MULTIPLE ACCOUNTS FOUND WITH THIS EMAIL // IN A PREVIOUS VERSION OF BS ACCOUNTS WERE ABLE TO SHARE EMAILS var errorObj = new ErrorObj(400, 'u0055', __filename, 'bsuser', 'a bsuser already exists with the email provided', err ); reject(errorObj); } else { resolve(); } }); } }); } async getUserFromApiToken (apiTkn) { return new Promise((resolve, reject) => { this.dataAccess.getSession(null, apiTkn) .then((sessionObj) => { if(sessionObj.anonymous) { return {'username': 'anonymous'}; } else { return this.dataAccess.getUserBySession(sessionObj.id); } }) .then((userObj) => { resolve(userObj); }) .catch((err) => { // ADD LOGGING HERE? if (err !== undefined && err !== null && typeof (err.AddToError) === 'function') { reject(err.AddToError(__filename, 'getUserFromApiToken')); } else { var errorObj = new ErrorObj(500, 'u1001', __filename, 'getUserFromApiToken', 'error getting user from api token', 'Error getting user from api token', err ); reject(errorObj); } }); }); } async copyFile(file_to_copy, destination_path){ return new Promise((resolve, reject) => { try { fs.createReadStream(file_to_copy).pipe(fs.createWriteStream(destination_path)); resolve({ 'success': true }); } catch (err) { var errorObj = new ErrorObj(500, 'u0002', __filename, 'copyFile', 'error with fs.createReadStream', 'External error', err ); reject(errorObj); } }); } async writeToFile(file_path, strData, isBinary) { return new Promise((resolve, reject) => { let binaryArg = isBinary ? 'binary' : null; fs.writeFile(file_path, strData, binaryArg, function (write_err) { if (write_err) { var errorObj = new ErrorObj(500, 'u0005', __filename, 'writeToFile', 'error with fs.writeToFile', 'External error', write_err ); reject(errorObj); } else { resolve(true); } } ); }); } async writeErrorToLog(errObj) { return new Promise((resolve, reject) => { let logEntry = JSON.stringify(errObj)+'\n'; var writeToLog = util.promisify(this.errorLog.write); writeToLog(logEntry) .then((write_res) => { resolve(); }) .catch((write_err) => { reject(write_err); }); }); } async sendMail(send_to, sbj, bdy, html_bdy, attachments, replyTo, inReplyTo, from) { return new Promise((resolve, reject) => { var mailOptions = { from: from || this.settings.mail_options.account, to: send_to, subject: sbj }; if(bdy) mailOptions.text = bdy; if(html_bdy) mailOptions.html = html_bdy; if(html_bdy == null && bdy == null) { reject(new ErrorObj(500, 'u0100', __filename, 'sendMail', 'no content', 'Attempt to send empty email.')); return; } if (replyTo) { mailOptions.replyTo = replyTo; } if (inReplyTo) { mailOptions.inReplyTo = inReplyTo; } if(attachments) { if(Array.isArray(attachments)) { mailOptions.attachments = attachments; } else if(typeof(attachments) === 'object') { mailOptions.attachments = [attachments]; } } this.mailTransport.sendMail(mailOptions, function (email_err, email_res) { if (!email_err) { resolve(email_res); } else { var errorObj = new ErrorObj(500, 'u0009', __filename, 'sendMail', 'error with mailTransport.sendMail', 'External error', email_err ); reject(errorObj); } }); }); } async sendMailTemplate(send_to, sbj, template_name, args, attachments, replyTo, inReplyTo, from) { return new Promise((resolve, reject) => { if (template_name === undefined || template_name === null) { template_name = 'default'; } if (args === undefined || args === null) { args = {}; } var templatePath = path.resolve(__dirname, this.settings.mail_options.template_directory + template_name); var txtPath = templatePath + '.txt'; var htmlPath = templatePath + '.html'; var foundTxt = true; var foundHtml = true; try { fs.accessSync(txtPath); } catch (e) { foundTxt = false; } try { fs.accessSync(htmlPath); } catch (e) { foundHtml = false; } var txtBody = ''; var htmlBody = ''; if (foundTxt && foundHtml) { fs.readFile(txtPath, 'utf8', (txt_err, txt_data) => { if (!txt_err) { txtBody = this.#replaceTemplateValues(txt_data, args) fs.readFile(htmlPath, 'utf8', (html_err, html_data) => { if (!html_err) { htmlBody = this.#replaceTemplateValues(html_data, args); var mailOptions = { from: from || this.settings.mail_options.account, to: send_to, subject: sbj, text: txtBody, html: htmlBody }; if (replyTo) { mailOptions.replyTo = replyTo; } if (inReplyTo) { mailOptions.inReplyTo = inReplyTo; } // ADD ATTACHMENT IF NECESSARY if(attachments) { if(Array.isArray(attachments)) { mailOptions.attachments = attachments; } else if(typeof(attachments) === 'object') { mailOptions.attachments = [attachments]; } } this.mailTransport.sendMail(mailOptions, function (email_err, email_res) { if (!email_err) { resolve(email_res); } else { var errorObj = new ErrorObj(500, 'u0011', __filename, 'sendMailTemplate', 'error with mailTransport.sendMail', 'External error', email_err ); reject(errorObj); } }); } else { // SOMETHING WENT WRONG WHILE READING THE HTML TEMPLATE var errorObj = new ErrorObj(500, 'u0012', __filename, 'sendMailTemplate', 'error reading html template', 'There was a problem getting the html template for this email', html_err ); reject(errorObj); } }); } else { // SOMETHING WENT WRONG WHILE READING THE TXT TEMPLATE var errorObj = new ErrorObj(500, 'u0013', __filename, 'sendMailTemplate', 'error reading text template', 'There was a problem getting the text template for this email', txt_err ); reject(errorObj); } }); } else if (foundTxt) { fs.readFile(txtPath, 'utf8', (txt_err, txt_data) => { if (!txt_err) { txtBody = this.#replaceTemplateValues(txt_data, args); var mailOptions = { from: from || this.settings.mail_options.account, to: send_to, subject: sbj, text: txtBody }; if (replyTo) { mailOptions.replyTo = replyTo; } if (inReplyTo) { mailOptions.inReplyTo = inReplyTo; } // ADD ATTACHMENT IF NECESSARY if(attachments) { if(Array.isArray(attachments)) { mailOptions.attachments = attachments; } else if(typeof(attachments) === 'object') { mailOptions.attachments = [attachments]; } } this.mailTransport.sendMail(mailOptions, function (email_err, email_res) { if (!email_err) { resolve(email_res); } else { var errorObj = new ErrorObj(500, 'u0014', __filename, 'sendMailTemplate', 'error with mailTransport.sendMail', 'External error', email_err ); reject(errorObj); } }); } else { // SOMETHING WENT WRONG WHILE READING THE TXT TEMPLATE var errorObj = new ErrorObj(500, 'u0015', __filename, 'sendMailTemplate', 'error reading text template', 'There was a problem getting the text template for this email', txt_err ); reject(errorObj); } }); } else if (foundHtml) { fs.readFile(htmlPath, 'utf8', (html_err, html_data) => { if (!html_err) { htmlBody = this.#replaceTemplateValues(html_data, args); var mailOptions = { from: from || this.settings.mail_options.account, to: send_to, subject: sbj, html: htmlBody }; if (replyTo) { mailOptions.replyTo = replyTo; } if (inReplyTo) { mailOptions.inReplyTo = inReplyTo; } // ADD ATTACHMENT IF NECESSARY if(attachments) { if(Array.isArray(attachments)) { mailOptions.attachments = attachments; } else if(typeof(attachments) === 'object') { mailOptions.attachments = [attachments]; } } this.mailTransport.sendMail(mailOptions, function (email_err, email_res) { if (!email_err) { resolve(email_res); } else { var errorObj = new ErrorObj(500, 'u0016', __filename, 'sendMailTemplate', 'error with mailTransport.sendMail', 'External error', email_err ); reject(errorObj); } }); } else { // SOMETHING WENT WRONG WHILE READING THE HTML TEMPLATE var errorObj = new ErrorObj(500, 'u0017', __filename, 'sendMailTemplate', 'error reading html template', 'There was a problem getting the html template for this email', html_err ); reject(errorObj); } }); } else { // WE COULDN'T FIND THIS TEMPLATE. var errorObj = new ErrorObj(500, 'u0018', __filename, 'sendMailTemplate', 'no template found', 'There was a problem locating the template file.', {template: template_name} ); reject(errorObj); } }); } #replaceTemplateValues(template, args) { var updatedTemplate = template; for(var key in args){ updatedTemplate = updatedTemplate.replace(new RegExp('{{' + key + '}}', 'g'), args[key]); } return updatedTemplate; } #createUID() { var tKey = crypto.randomBytes(12).toString('hex'); var date = new Date(); var dateKey = new Date(date.getFullYear(), date.getMonth(), date.getDay(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()); var token = crypto.createHash("md5").update(tKey + dateKey).digest('hex'); return token; } getUID(sync) { if(sync == null || sync === false) { return Promise.resolve(this.#createUID()); } else { return this.#createUID(); } } async logEvent(tkn, eventDescriptor) { var loggedEvent = { 'token': tkn, 'event_data': eventDescriptor }; let logEntry = JSON.stringify(loggedEvent)+'\n'; this.eventLog.write(logEntry, () => { return Promise.resolve(); }); } async invalidateSession(sessionObj) { return new Promise((resolve, reject) => { this.dataAccess.DeleteSessions([sessionObj.id]) .then(() => { if(this.settings.session_logging === true) { let dsObj = { session_id: sessionObj.id, token: sessionObj.token, user_id: sessionObj.user_id, started_at: sessionObj.started_at, ended_at: new Date() } var logEntry = JSON.stringify(dsObj)+'\n'; this.sessionLog.write(logEntry); } resolve(); }) .catch((err) => { reject(err.AddToError(__filename, 'invalidateSession')); }); }); } htmlify(obj, idx) { if(idx === undefined || idx === null || typeof(idx) !== 'number') { idx = 0; } var pList = Object.getOwnPropertyNames(obj); var indentString = ''; for(var iIdx = 0; iIdx < idx; iIdx++) { indentString += '&nbsp;&nbsp;&nbsp;&nbsp;'; } var newHtmlString = ''; for(var pIdx = 0; pIdx < pList.length; pIdx++) { var propName = pList[pIdx]; if(typeof(obj[propName]) !== 'object') { newHtmlString += indentString + propName + ': ' + obj[propName] + '<br />'; } else if(obj[propName] !== undefined && obj[propName] !== null) { newHtmlString += indentString + propName + ':<br />'; var nextIdx = idx+1; newHtmlString += this.htmlify(obj[propName], nextIdx); } else { newHtmlString += indentString + propName + ': null' + '<br />'; } } if(idx === 0) { newHtmlString = '<div>' + newHtmlString + '</div>'; } return newHtmlString; } } const instance = new Utilities(); module.exports = instance;