UNPKG

jsharmony

Version:

Rapid Application Development (RAD) Platform for Node.js Database Application Development

628 lines (592 loc) 26.8 kB
/* Copyright 2017 apHarmony This file is part of jsHarmony. jsHarmony is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jsHarmony 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this package. If not, see <http://www.gnu.org/licenses/>. */ var Helper = require('./lib/Helper.js'); var _ = require('lodash'); var async = require('async'); var crypto = require('crypto'); var moment = require('moment'); module.exports = exports = {}; exports.ExecDBFunc = function (dbfunc, context, sql, ptypes, params, callback, dbconfig, db, trans) { var _this = this; if(!db) db = _this.jsh.getDB('default'); if(trans){ db.ExecTransTasks([function (dbtrans, cb, transtbl) { dbfunc.call(db, context, sql, ptypes, params, dbtrans, function (err, rslt, stats) { cb(err, rslt, stats); }, dbconfig); }], callback); } else { db.ExecTasks([function (cb) { dbfunc.call(db, context, sql, ptypes, params, undefined, function (err, rslt, stats) { cb(err, rslt, stats); }, dbconfig); }], callback); } }; exports.ExecRecordset = function (context, sql, ptypes, params, callback, dbconfig, db, trans) { if(!db) db = this.jsh.getDB('default'); this.ExecDBFunc(db.Recordset, context, sql, ptypes, params, callback, dbconfig, db, trans); }; exports.ExecRecordsetTrans = function (context, sql, ptypes, params, callback, dbconfig, db) { return this.ExecRecordset(context, sql, ptypes, params, callback, dbconfig, db, true); }; exports.ExecMultiRecordset = function (context, sql, ptypes, params, callback, dbconfig, db, trans) { if(!db) db = this.jsh.getDB('default'); this.ExecDBFunc(db.MultiRecordset, context, sql, ptypes, params, callback, dbconfig, db, trans); }; exports.ExecMultiRecordsetTrans = function (context, sql, ptypes, params, callback, dbconfig, db) { return this.ExecMultiRecordset(context, sql, ptypes, params, callback, dbconfig, db, true); }; exports.ExecRow = function (context, sql, ptypes, params, callback, dbconfig, db, trans) { if(!db) db = this.jsh.getDB('default'); this.ExecDBFunc(db.Row, context, sql, ptypes, params, callback, dbconfig, db, trans); }; exports.ExecRowTrans = function (context, sql, ptypes, params, callback, dbconfig, db) { return this.ExecRow(context, sql, ptypes, params, callback, dbconfig, db, true); }; exports.ExecCommand = function (context, sql, ptypes, params, callback, dbconfig, db, trans) { if(!db) db = this.jsh.getDB('default'); this.ExecDBFunc(db.Command, context, sql, ptypes, params, callback, dbconfig, db, trans); }; exports.ExecCommandTrans = function (context, sql, ptypes, params, callback, dbconfig, db) { return this.ExecCommand(context, sql, ptypes, params, callback, dbconfig, db, true); }; exports.ExecScalar = function (context, sql, ptypes, params, callback, dbconfig, db, trans) { if(!db) db = this.jsh.getDB('default'); this.ExecDBFunc(db.Scalar, context, sql, ptypes, params, callback, dbconfig, db, trans); }; exports.ExecScalarTrans = function (context, sql, ptypes, params, callback, dbconfig, db) { return this.ExecScalar(context, sql, ptypes, params, callback, dbconfig, db, true); }; exports.GetDBErrorMessage = function(dberrors, errmsg){ if(!dberrors) return null; errmsg = (errmsg||'').toString().toLowerCase(); for (var i = 0; i < dberrors.length; i++) { var dberr = dberrors[i]; var erex = dberr[0].toString().toLowerCase(); var etxt = dberr[1]; if (erex.indexOf('/') == 0) { erex = erex.substr(1, erex.length - 2); if (errmsg.match(new RegExp(erex))) { return etxt; } } else if (errmsg.indexOf(erex) >= 0) { return etxt; } } return null; }; exports.AppDBError = function (req, res, err, stats, errorHandler) { if(err.stats) stats = err.stats; if(!errorHandler) errorHandler = function(num, txt, stats){ return Helper.GenError(req, res, num, txt, { stats: stats }); }; if ('model' in err) { var model = err.model; if ('dberrors' in model) { var dberrormsg = exports.GetDBErrorMessage(model.dberrors, err.message); if(dberrormsg !== null) { return errorHandler(-9, dberrormsg, stats); } } } //Not necessary because sql is printed out in node debug in log below //if ('sql' in err) { if (this.jsh.Config.debug_params.appsrv_logsql) err.message += ' SQL: ' + err.sql; } if ((err.message) && (err.message == 'INVALID ACCESS')) return errorHandler(-12, 'Invalid DataLock Access', stats); if(err.message){ if(err.message.indexOf('Application Error - ') == 0) return errorHandler(-5, err.message, stats); if(err.message.indexOf('Application Warning - ') == 0) return errorHandler(-5, err.message, stats); if(err.message.indexOf('Execute Form - ') == 0) return errorHandler(-5, err.message, stats); } if(('number' in err) && err.frontend_visible) return errorHandler(err.number, err.message, stats); //if ('number' in err) return errorHandler(err.number, err.message); //This would prevent show_system_errors from functioning return errorHandler(-99999, err.message, stats); }; exports.DeformatParam = function (field, val, verrors) { function add_verror(verrors, err) { if (!('' in verrors)) verrors[''] = []; verrors[''].push(err); } if ((field.type == 'date') || (field.type == 'datetime')) { return (function(){ if (val === '') return null; if (val === null) return null; var dtstmp = Date.parse(val); if (isNaN(dtstmp)) { add_verror(verrors, field.name + ': Invalid Date'); return ''; } //Get time in original timezone var has_timezone = false; if (/Z|[+\-][0-9]+:[0-9]+$/.test(val)) has_timezone = true; // eslint-disable-line no-useless-escape var mtstmp = null; if (has_timezone) mtstmp = moment.parseZone(val); else mtstmp = moment(Helper.ParseDate(val)); if (!mtstmp.isValid()) { add_verror(verrors, field.name + ': Invalid Date'); return ''; } if (mtstmp.year()>9999) { add_verror(verrors, field.name + ': Invalid Date'); return ''; } if (mtstmp.year()<1753) { add_verror(verrors, field.name + ': Invalid Date'); return ''; } //Remove timezone, unless we need to preserve it var dtrslt = null; if(field.type=='date'){ dtrslt = moment(mtstmp.format('YYYY-MM-DDTHH:mm:ss.SSS')).toDate(); } else if(field.type=='datetime'){ if(field.datatype_config.preserve_timezone){ //If no timezone specified, set to UTC if(!has_timezone) mtstmp = moment.parseZone(mtstmp.format('YYYY-MM-DDTHH:mm:ss.SSS')+'Z'); dtrslt = mtstmp.toDate(); dtrslt.jsh_utcOffset = -1*mtstmp.utcOffset(); } else dtrslt = moment(mtstmp.format('YYYY-MM-DDTHH:mm:ss.SSS')).toDate(); //Get microseconds if(val){ var re_micros_date = /:\d\d\.\d\d\d(\d+)/.exec(val); if(re_micros_date){ dtrslt.jsh_microseconds = parseFloat('0.'+re_micros_date[1]) * 1000; } } } return dtrslt; //return mtstmp.format("YYYY-MM-DDTHH:mm:ss.SSS")+'Z'; //return mtstmp.toDate(); //return new Date(dtstmp); })(); } else if (field.type == 'time') { return (function(){ if (val === '') return null; if (val === null) return null; var fulldate = false; var dtstmp = Date.parse('1970-01-01 ' + val); if (isNaN(dtstmp)) { dtstmp = Date.parse(val); fulldate = true; } if (isNaN(dtstmp)) { add_verror(verrors, field.name + ': Invalid Time'); return ''; } var dt = new Date('1970-01-01'); //Get time in original timezone var has_timezone = false; if (/Z|[+\-][0-9]+:[0-9]+$/.test(val)) has_timezone = true; // eslint-disable-line no-useless-escape var mtstmp = null; var prefix = (!fulldate?'1970-01-01 ':''); if (has_timezone) mtstmp = moment.parseZone(prefix + val); else mtstmp = moment(Helper.ParseDate(prefix + val)); if(field.datatype_config.preserve_timezone){ //If no timezone specified, set to UTC if(!has_timezone) mtstmp = moment.parseZone(mtstmp.format('YYYY-MM-DDTHH:mm:ss.SSS')+'Z'); dt = mtstmp.toDate(); dt.jsh_utcOffset = -1*mtstmp.utcOffset(); } else dt = moment(mtstmp.format('YYYY-MM-DDTHH:mm:ss.SSS')).toDate(); //Get microseconds if(val){ var re_micros_time = /:\d\d\.\d\d\d(\d+)/.exec(val); if(re_micros_time){ dt.jsh_microseconds = parseFloat('0.'+re_micros_time[1]) * 1000; } } return dt; })(); } else if (field.type == 'encascii') { //return Helper.stringToASCIIBuffer(val); return Buffer.from(val, 'ascii'); } else if (field.type == 'binary') { if(!val) return null; if(val && (val.toString().substr(0,2).toLowerCase()=='0x')){ val = val.toString().substr(2); if(val.length % 2 == 1) val = val + '0'; return Buffer.from(val, 'hex'); } return Buffer.from(val, 'ascii'); } else if (field.type == 'boolean') { if (val === '') return null; if (val === null) return null; if (typeof val == 'undefined') return null; if (val === false) return false; if (val === true) return true; var valstr = val.toString().toUpperCase(); if((valstr==='TRUE')||(valstr==='T')||(valstr==='Y')||(valstr==='YES')||(valstr==='ON')||(valstr==='1')) return true; if((valstr==='FALSE')||(valstr==='F')||(valstr==='N')||(valstr==='NO')||(valstr==='OFF')||(valstr==='0')) return false; return null; /* if (!val) return false; if (val == '0') return false; if (val == 0) return false; return true; */ } return val; }; //Static function - jsh parameter required for static call exports.getSQLParameters = function(sql, fields, jsh){ if(!jsh) jsh = this.jsh; if(!jsh) throw new Error('jsh parameter must be defined'); if(!jsh.Config.system_settings.automatic_parameters) return []; var rslt = []; var _this = this; //Sort keys descending var ivars = []; _.each(fields, function(field){ if(!field.name) return; if(!field.type) return; if(field.type == 'file') return; ivars.push(field.name); }); ivars.sort(function (a, b) { return b.length - a.length; }); var usql = sql.toUpperCase(); //Search SQL for missing parameters that are defined as fields var sql_terminators = _this.SQL_TERMINATORS; for(var i=0;i<ivars.length;i++){ var ivar = ivars[i]; //Check if the iparamname is in usql var lastidx = null; var uivar = '@'+ivar.toUpperCase(); var foundvar = false; while(!foundvar && (lastidx != -1)){ lastidx = usql.indexOf(uivar,(lastidx===null?0:lastidx+1)); if(lastidx >= 0){ var nextchar = ''; if(usql.length > (lastidx+uivar.length)) nextchar = usql[lastidx+uivar.length]; if((nextchar==='') || (sql_terminators.indexOf(nextchar)>=0)) foundvar = true; } } if(foundvar){ //Add missing variable rslt.push(ivar); } } return rslt; }; exports.ApplyAutomaticSQLParameters = function(ivars, sql, sql_ptypes, sql_params, fields){ if(!ivars) return; if(!this.jsh.Config.system_settings.automatic_parameters) return; var _this = this; //Sort keys descending var ikeys = _.keys(ivars); ikeys.sort(function (a, b) { return b.length - a.length; }); var usql = sql.toUpperCase(); //Search SQL for missing parameters that are in ivars var sql_terminators = _this.SQL_TERMINATORS; for(var i=0;i<ikeys.length;i++){ var ivar = ikeys[i]; if(ivar in sql_params) continue; //Check if the iparamname is in usql var lastidx = null; var uivar = '@'+ivar.toUpperCase(); var foundvar = false; while(!foundvar && (lastidx != -1)){ lastidx = usql.indexOf(uivar,(lastidx===null?0:lastidx+1)); if(lastidx >= 0){ var nextchar = ''; if(usql.length > (lastidx+uivar.length)) nextchar = usql[lastidx+uivar.length]; if((nextchar==='') || (sql_terminators.indexOf(nextchar)>=0)) foundvar = true; } } if(foundvar){ //Add missing variable var field = _this.getFieldByName(fields, ivar); if(field){ sql_ptypes.push(_this.getDBType(field)); sql_params[ivar] = ivars[ivar]; } } } }; exports.ApplyQueryParameters = function(Q, sql, sql_ptypes, sql_params, model){ if(!Q) return; this.ApplyAutomaticSQLParameters(Q, sql, sql_ptypes, sql_params, model.fields); }; exports.getTransVars = function(transtbl){ var transvars = {}; if(transtbl){ for(var tblid in transtbl){ if(transtbl[tblid]){ var tbl = transtbl[tblid]; if(_.isString(tbl)) continue; if (!_.isArray(tbl) || tbl.length < 2) transvars = _.extend(transvars, tbl); } } } return transvars; }; exports.ApplyTransTblEscapedParameters = function(sql_params, transtbl) { if (typeof transtbl == 'undefined') return sql_params; var transvars = this.getTransVars(transtbl); for (var pname in sql_params) { if ((sql_params[pname] == '%%%' + pname + '%%%') && (pname in transvars)) { sql_params[pname] = transvars[pname]; } } return sql_params; }; exports.ApplyTransTblChainedParameters = function(transtbl, sql, sql_ptypes, sql_params, fields){ if(!transtbl) return; var transvars = this.getTransVars(transtbl); for (var sql_param in sql_params) { if ((sql_params[sql_param] === null) && (sql_param in transvars)){ sql_params[sql_param] = transvars[sql_param]; } } this.ApplyAutomaticSQLParameters(transtbl, sql, sql_ptypes, sql_params, fields); }; exports.addSearchTerm = function (req, model, field, search_i, in_search_value, comparison, sql_ptypes, sql_params, verrors, options) { var _this = this; var db = _this.jsh.getModelDB(req, model.id); if(!options) options = {}; if (!('type' in field)) throw new Error('Search term ' + field.name + ' must have type.'); var ftype = field.type; var pname = 'search_' + search_i + '_' + field.name; if ((comparison == 'null') || (comparison == 'notnull')) { in_search_value = ''; } else { //Validate search parameter switch (field.type) { case 'boolean': break; case 'bigint': case 'int': case 'smallint': case 'tinyint': if (isNaN(in_search_value)) return ''; if ((parseFloat(in_search_value)%1) != 0) return ''; break; case 'float': case 'decimal': if (isNaN(in_search_value)) return ''; break; case 'varchar': case 'char': break; case 'datetime': case 'date': var dtval = this.DeformatParam(field, in_search_value, {}); if (!dtval) return ''; //Check if this is a date in_search_value = dtval; //new Date(moment.utc(in_search_value).format("YYYY-MM-DDTHH:mm:ss.SSS") + 'Z'); break; case 'time': var tmval = this.DeformatParam(field, in_search_value, {}); if (!tmval) return ''; in_search_value = tmval; break; case 'hash': //Generate Hash if (!(field.salt in this.jsh.Config.salts)) throw new Error('Hash salt not defined.'); in_search_value = crypto.createHash('sha1').update(in_search_value + this.jsh.Config.salts[field.salt]).digest(); in_search_value = this.DeformatParam(field, in_search_value, {}); break; case 'binary': if(in_search_value){ in_search_value = in_search_value.toString(); var wild_start = (in_search_value.substr(0,1)=='%'); if(wild_start) in_search_value = in_search_value.substr(1); var wild_end = (in_search_value && (in_search_value.substr(in_search_value.length-1,1)=='%')); if(wild_end) in_search_value = in_search_value.substr(0,in_search_value.length - 1); if(in_search_value.substr(0,2).toLowerCase()=='0x') in_search_value = in_search_value.substr(2).toUpperCase(); else if(in_search_value) in_search_value = Helper.str2hex(in_search_value).toUpperCase(); else in_search_value = ''; if(wild_start) in_search_value = '%' + in_search_value; if(wild_end) in_search_value = in_search_value + '%'; } else in_search_value = null; break; default: throw new Error('Search type ' + field.name + '/' + ftype + ' not supported.'); } } var searchterm = db.sql.getSearchTerm(this.jsh, model, field, pname, in_search_value, comparison); if (searchterm) { if (!searchterm.dbtype) searchterm.dbtype = _this.getDBType(field); //Don't deformat dates if ((ftype != 'datetime') && (ftype != 'date') && (ftype != 'time') && (ftype != 'binary')) searchterm.search_value = this.DeformatParam(field, searchterm.search_value, verrors); if((searchterm.search_value === null) && ((comparison != 'null') && (comparison != 'notnull'))){ if(options.search_all) return ''; else { if(!verrors['']) verrors[''] = []; verrors[''].push('Invalid search value for ' + field.name + ' (' + ftype + ')'); } } sql_ptypes.push(searchterm.dbtype); sql_params[pname] = searchterm.search_value; if(comparison=='soundslike'){ sql_ptypes.push(_this.DB.types.VarChar(4)); sql_params[pname+'_soundex'] = _this.DB.util.Soundex((searchterm.search_value||'').toString()); } return searchterm.sql; } return ''; }; exports.getDataLockSQL = function (req, model, fields, sql_ptypes, sql_params, verrors, fPerDataLock, nodatalock, descriptor, options) { if (!('datalock' in req.jshsite)) return; if(!descriptor){ if(model) descriptor = model.id; else descriptor = ''; } descriptor = (descriptor ? ' (' + descriptor + ')' : ''); options = _.extend({ skipDataLocks: [] }, options); var _this = this; if (!nodatalock) nodatalock = ''; var arrayOptions = {}; if(this.jsh.Config.system_settings.case_insensitive_datalocks) arrayOptions.caseInsensitive = true; for (var datalockid in req.jshsite.datalock) { if ((typeof nodatalock != 'undefined') && (Helper.arrayIndexOf(nodatalock,datalockid,arrayOptions) >= 0)) continue; if(model && Helper.arrayIndexOf(model.nodatalock,datalockid,arrayOptions) >= 0) continue; var datalock_options = this.jsh.Config.datalock_options && this.jsh.Config.datalock_options[req.jshsite.id] && Helper.arrayItem(this.jsh.Config.datalock_options[req.jshsite.id],datalockid,arrayOptions); if(model && datalock_options && datalock_options.for){ if(!_.includes(datalock_options.for, model.id)) continue; } var found_datalock = false; var datalockval = req.jshsite.datalock[datalockid](req); for (var i = 0; i < fields.length; i++) { var field = fields[i]; if(Helper.arrayIndexOf(options.skipDataLocks,field.name,arrayOptions) >= 0) continue; if ('datalock' in field) { var datalockqueryid = Helper.arrayItem(field.datalock,datalockid,arrayOptions); if (datalockqueryid) { if (!('datalocks' in this.jsh.Config)) throw new Error('No datalocks in config'); if(!(req.jshsite.id in this.jsh.Config.datalocks)) throw new Error("Site '"+req.jshsite.id+"' not defined in datalocks"); var datalocks = Helper.arrayItem(this.jsh.Config.datalocks[req.jshsite.id],datalockid,arrayOptions); if(!datalocks) throw new Error("Datalock '"+datalockid+"' not defined in site datalocks"); var datalockquery = Helper.arrayItem(datalocks,datalockqueryid,arrayOptions); if(datalockquery === '') continue; if (!datalockquery) throw new Error("Datalock query '" + datalockqueryid + "' not defined in config for site "+req.jshsite.id+', datalock: '+datalockid); var frslt = fPerDataLock(datalockquery, field); if ((typeof frslt !== 'undefined') && (frslt === false)) continue; found_datalock = true; //Add field to parameters var datalockparamname = 'datalock_' + datalockid; if (!(datalockparamname in sql_params)) { if (!('datalocktypes' in req.jshsite)) throw new Error('Missing datalocktypes in config ' + descriptor); if (!(datalockid in req.jshsite.datalocktypes)) throw new Error('Missing DataLock type for ' + datalockid + descriptor); var datalocktype = req.jshsite.datalocktypes[datalockid]; var dbtype = _this.getDBType(datalocktype); sql_ptypes.push(dbtype); sql_params[datalockparamname] = this.DeformatParam(datalocktype, datalockval, verrors); } } } else if (field.key) throw new Error('Missing DataLock for key ' + field.name + descriptor); else if (('actions' in field) && (Helper.hasAction(field.actions, 'F'))) throw new Error('Missing DataLock for foreign key ' + field.name + descriptor); else if (('actions' in field) && (Helper.hasAction(field.actions, 'C'))) throw new Error('Missing DataLock for initial parameter ' + field.name + descriptor); } //if(!found_datalock){ this.jsh.Log.debug(fields); } //Use for debugging if (!found_datalock) throw new Error('No fields with DataLock ' + datalockid + ' found in ' + descriptor); } }; exports.getDBType = function (field) { var _this = this; var fname = field.name; if (!('type' in field)) throw new Error('Field ' + fname + ' must have type.'); var ftype = field.type; var flen = field.length; if (ftype == 'bigint') return _this.DB.types.BigInt; else if (ftype == 'varchar') { if ((typeof flen == 'undefined') || (flen==-1)) flen = _this.DB.types.MAX; return _this.DB.types.VarChar(flen); } else if (ftype == 'char') { if ((typeof flen == 'undefined') || (flen==-1)) flen = _this.DB.types.MAX; return _this.DB.types.Char(flen); } else if (ftype == 'datetime') { return _this.DB.types.DateTime(field.precision, field.datatype_config.preserve_timezone); } else if (ftype == 'time') { return _this.DB.types.Time(field.precision, field.datatype_config.preserve_timezone); } else if (ftype == 'date') return _this.DB.types.Date; else if (ftype == 'decimal') { var prec_h = 38; var prec_l = 10; if ('precision' in field) { prec_h = field.precision[0]; prec_l = field.precision[1]; } return _this.DB.types.Decimal(prec_h, prec_l); } else if (ftype == 'float') { var prec = 53; if ('precision' in field) { prec = field.precision; } return _this.DB.types.Float(prec); } else if (ftype == 'int') return _this.DB.types.Int; else if (ftype == 'smallint') return _this.DB.types.SmallInt; else if (ftype == 'tinyint') return _this.DB.types.TinyInt; else if (ftype == 'boolean') return _this.DB.types.Boolean; else if ((ftype == 'hash') || (ftype == 'encascii')) { if (typeof flen == 'undefined') throw new Error('Field ' + fname + ' must have length.'); return _this.DB.types.VarBinary(flen); } else if(ftype == 'binary'){ if ((typeof flen == 'undefined') || (flen==-1)) flen = _this.DB.types.MAX; return _this.DB.types.VarBinary(flen); } else if (ftype == 'raw') return _this.DB.types.Raw; else throw new Error('Key ' + fname + ' has invalid type: '+(field.type||'').toString()); }; exports.TransformDBTasks = function(collection, f){ return _.transform(collection, function(accumulator, value, key){ if(!_.isFunction(value)){ var childarr = exports.TransformDBTasks(value, f, {}); if(!_.isEmpty(childarr)) accumulator[key] = childarr; //else delete accumulator[key]; //Transform does not require this } else { var newf = f(value, key); if(typeof newf != 'undefined') accumulator[key] = newf; //else delete accumulator[key]; //Transform does not require this } }); }; exports.ExecTasks = function (req, res, dbtasks, trans, callback, options) { options = _.extend({ db: undefined }, options); /* dbtasks is an array of: function(dbtrans, callback, transtbl){ ... } */ var _this = this; if (_.isEmpty(dbtasks)) { res.type('json'); res.end(JSON.stringify({ '_success': 1, '_stats': {} })); return; } //Split off post-processing var posttasks = []; dbtasks = _this.TransformDBTasks(dbtasks, function(dbtask, key){ key = key.toString(); if (Helper.endsWith(key, '_POSTPROCESS')) posttasks.push(dbtask); else return dbtask; }); var db = options.db || _this.jsh.getDB('default'); var dbfunc = db.ExecTasks; if ((typeof trans != 'undefined') && trans) dbfunc = db.ExecTransTasks; else{ /* For db.ExecTasks, dbtasks is converted to an array of: dbtask = function(cb, transtbl){ ... } dbtrans is set to undefined */ dbtasks = _this.TransformDBTasks(dbtasks, function(dbtask, key){ return async.apply(dbtask, undefined); }); } dbfunc.call(db, dbtasks, function (err, rslt, stats) { if (err != null) { _this.AppDBError(req, res, err, stats); return; } if (rslt == null) rslt = {}; rslt['_success'] = 1; rslt['_stats'] = Helper.FormatStats(req, stats); //Handle EOF for (var rs in rslt) { if (_.isArray(rslt[rs]) && (_.isObject(rslt[rs][rslt[rs].length - 1])) && ('_eof' in rslt[rs][rslt[rs].length - 1])) { var eof = rslt[rs].pop(); rslt['_eof_' + rs] = eof._eof; } } //Convert buffers to hex strings Helper.convertBufferToHexString(rslt); //Run POSTPROCESS tasks async.eachSeries(posttasks, function (posttask, postcallback) { posttask(postcallback, rslt); }, function (err) { if (err != null) { _this.AppDBError(req, res, err); return; } res.type('json'); res.send(JSON.stringify(rslt)); if (typeof callback != 'undefined') callback(); }); }); }; return module.exports;