UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

251 lines (224 loc) 8.84 kB
'use strict'; var fs = require('fs'); var path = require('path'); module.exports = { loadPriority: 0, initialize: function(api, next){ if(!api.utils){ api.utils = {}; } //////////////////////////////////////////////////////////////////////////// // merge two hashes recursively api.utils.hashMerge = function(a, b, arg){ var c = {}; var i; var response; for(i in a){ if(api.utils.isPlainObject(a[i]) && Object.keys(a[i]).length > 0){ c[i] = api.utils.hashMerge(c[i], a[i], arg); }else{ if(typeof a[i] === 'function'){ response = a[i](arg); if(api.utils.isPlainObject(response)){ c[i] = api.utils.hashMerge(c[i], response, arg); }else{ c[i] = response; } }else{ c[i] = a[i]; } } } for(i in b){ if(api.utils.isPlainObject(b[i]) && Object.keys(b[i]).length > 0){ c[i] = api.utils.hashMerge(c[i], b[i], arg); }else{ if(typeof b[i] === 'function'){ response = b[i](arg); if(api.utils.isPlainObject(response)){ c[i] = api.utils.hashMerge(c[i], response, arg); }else{ c[i] = response; } }else{ c[i] = b[i]; } } } return c; }; api.utils.isPlainObject = function(o){ var safeTypes = [Boolean, Number, String, Function, Array, Date, RegExp, Buffer]; var safeInstances = ['boolean', 'number', 'string', 'function']; var expandPreventMatchKey = '_toExpand'; // set `_toExpand = false` within an object if you don't want to expand it var i; if(!o){ return false; } if((o instanceof Object) === false){ return false; } for(i in safeTypes){ if(o instanceof safeTypes[i]){ return false; } } for(i in safeInstances){ if(typeof o === safeInstances[i]){ return false; } } if(o[expandPreventMatchKey] === false){ return false; } return (o.toString() === '[object Object]'); }; //////////////////////////////////////////////////////////////////////////// // string to hash // http://stackoverflow.com/questions/6393943/convert-javascript-string-in-dot-notation-into-an-object-reference api.utils.stringToHash = function(path, object){ if(!object){ object = api; } function _index(obj, i){ return obj[i]; } return path.split('.').reduce(_index, object); }; //////////////////////////////////////////////////////////////////////////// // unique-ify an array api.utils.arrayUniqueify = function(arr){ var a = []; for(var i = 0; i < arr.length; i++){ for(var j = i + 1; j < arr.length; j++){ if(arr[i] === arr[j]){ j = ++i; } } a.push(arr[i]); } return a; }; //////////////////////////////////////////////////////////////////////////// // get all .js files in a directory api.utils.recursiveDirectoryGlob = function(dir, extension, followLinkFiles){ var results = []; if(!extension){ extension = '.js'; } if(!followLinkFiles){ followLinkFiles = true; } extension = extension.replace('.', ''); if(dir[dir.length - 1] !== path.sep){ dir += path.sep; } if(fs.existsSync(dir)){ fs.readdirSync(dir).forEach(function(file){ var fullFilePath = path.normalize(dir + file); if(file[0] !== '.'){ // ignore 'system' files var stats = fs.statSync(fullFilePath); var child; if(stats.isDirectory()){ child = api.utils.recursiveDirectoryGlob(fullFilePath, extension, followLinkFiles); child.forEach(function(c){ results.push(c); }); }else if(stats.isSymbolicLink()){ var realPath = fs.readlinkSync(fullFilePath); child = api.utils.recursiveDirectoryGlob(realPath, extension, followLinkFiles); child.forEach(function(c){ results.push(c); }); }else if(stats.isFile()){ var fileParts = file.split('.'); var ext = fileParts[(fileParts.length - 1)]; // real file match if(ext === extension){ results.push(fullFilePath); } // linkfile traversal if(ext === 'link' && followLinkFiles === true){ var linkedPath = api.utils.sourceRelativeLinkPath(fullFilePath, api.config.general.paths.plugin); if(linkedPath){ child = api.utils.recursiveDirectoryGlob(linkedPath, extension, followLinkFiles); child.forEach(function(c){ results.push(c); }); }else{ try{ api.log(['cannot find linked refrence to `%s`', file], 'warning'); }catch(e){ throw('cannot find linked refrence to' + file); } } } } } }); } return results.sort(); }; api.utils.sourceRelativeLinkPath = function(linkfile, pluginPaths){ var type = fs.readFileSync(linkfile).toString(); var pathParts = linkfile.split(path.sep); var name = pathParts[(pathParts.length - 1)].split('.')[0]; var pathsToTry = pluginPaths.slice(0); var pluginRoot; // TODO: always also try the local destination's `node_modules` to allow for nested plugins // This might be a security risk without requiring explicit sourcing pathsToTry.forEach(function(pluginPath){ var pluginPathAttempt = path.normalize(pluginPath + path.sep + name); try{ var stats = fs.lstatSync(pluginPathAttempt); if(!pluginRoot && stats.isDirectory()){ pluginRoot = pluginPathAttempt; } }catch(e){ } }); if(!pluginRoot){ return false; } var pluginSection = path.normalize(pluginRoot + path.sep + type); return pluginSection; }; //////////////////////////////////////////////////////////////////////////// // object Clone api.utils.objClone = function(obj){ return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyNames(obj).reduce(function(memo, name){ return (memo[name] = Object.getOwnPropertyDescriptor(obj, name)) && memo; }, {})); }; //////////////////////////////////////////////////////////////////////////// // attempt to collapse this object to an array; ie: {"0": "a", "1": "b"} api.utils.collapseObjectToArray = function(obj){ try{ var keys = Object.keys(obj); if(keys.length < 1){ return false; } if(keys[0] !== '0'){ return false; } if(keys[(keys.length - 1)] !== String(keys.length - 1)){ return false; } var arr = []; for(var i in keys){ var key = keys[i]; if(String(parseInt(key)) !== key){ return false; } else{ arr.push(obj[key]); } } return arr; }catch(e){ return false; } }; //////////////////////////////////////////////////////////////////////////// // get this servers external interface api.utils.getExternalIPAddress = function(){ var os = require('os'); var ifaces = os.networkInterfaces(); var ip = false; for(var dev in ifaces){ ifaces[dev].forEach(function(details){ if(details.family === 'IPv4' && details.address !== '127.0.0.1'){ ip = details.address; } }); } return ip; }; //////////////////////////////////////////////////////////////////////////// // cookie parse from headers of http(s) requests api.utils.parseCookies = function(req){ var cookies = {}; if(req.headers.cookie){ req.headers.cookie.split(';').forEach(function(cookie){ var parts = cookie.split('='); cookies[parts[0].trim()] = (parts[1] || '').trim(); }); } return cookies; }; //////////////////////////////////////////////////////////////////////////// // parse an IPv6 address // https://github.com/evantahler/actionhero/issues/275 && https://github.com/nullivex api.utils.parseIPv6URI = function(addr){ var host = '::1'; var port = '80'; var regexp = new RegExp(/\[([0-9a-f:]+)\]:([0-9]{1,5})/); //if we have brackets parse them and find a port if(addr.indexOf('[') > -1 && addr.indexOf(']') > -1){ var res = regexp.exec(addr); if(res === null){ throw new Error('failed to parse address'); } host = res[1]; port = res[2]; }else{ host = addr; } return {host: host, port: parseInt(port, 10)}; }; next(); } };