sicarii
Version:
The zero dependency http2 nodejs multithreading framework
1,370 lines (1,161 loc) • 34.7 kB
JavaScript
const http2 = require('http2'),
fs = require('fs'),
cwd = process.cwd(),
config = require(process.env.config_file),
pre_cache = require(cwd + config.pre_cache),
push_config = require(cwd + config.push_handler.path),
ip_config = require(cwd + config.ip_config + '.json'),
path = require('path'),
zlib = require('zlib'),
crypto = require('crypto'),
crypt = require('./crypt'),
qs = require('querystring'),
url = require('url'),
dns = require('dns');
const utils = {
engine: {
add: function(title, obj, cb){
try {
let adapt_file = __dirname.split('/').slice(0, -1).join('/');
adapt_file += '/adapters/'+ title +'.js'
let tpl =
'const '+ title +' = require("'+ title +'"),\nconfig = require(process.env.config_file),\nutils = require("../utils");\n\nmodule.exports = function(stream, file, src, url, data, cb){\n utils.render_sort(stream, data, url, cb);\n};'
config.template_engine.engines.push(title);
config.template_engine[title] = obj;
fs.writeFile(process.env.config_file +'.json', JSON.stringify(config), function(err){
if(err){return cb(err)}
utils.cc(['engine', title + ' config added to config file'],92)
fs.writeFile(adapt_file, tpl, function(err){
if(err){return cb(err)}
utils.cc(['engine', title + ' adapter template created'],92)
cb(false)
})
})
} catch (err) {
cb(err)
}
},
del: function(arr,cb){
try {
let adapt_file = __dirname.split('/').slice(0, -1).join('/');
for (let i = 0; i < arr.length; i++) {
let file = adapt_file + '/adapters/'+ arr[i] +'.js',
new_arr = [],
items = config.template_engine.engines;
for (let x = 0; x < items.length; x++) {
if(items[x] !== arr[i]){
new_arr.push(items[x])
}
}
config.template_engine.engines = new_arr;
delete config.template_engine[arr[i]];
fs.unlink(file, function(err){
if(err){return cb(err)}
utils.cc(['engine', arr[i] + ' adapter removed'],92)
if(i === arr.length -1){
fs.writeFile(process.env.config_file +'.json', JSON.stringify(config), function(err){
if(err){return cb(err)}
utils.cc(['engine', 'config file updated'],92)
cb(false)
})
}
})
}
} catch (err) {
cb(err)
}
}
},
upload: function(obj, cb){
try {
let base = cwd + config.uploads.path,
dest = path.dirname(obj.path),
file = path.basename(obj.path),
ext = path.extname(file).slice(1),
mtype = config.uploads.mimetypes,
gz = obj.gzip,
br = obj.brotli,
dfl = obj.deflate;
if(gz === undefined){
gz = config.uploads.gzip
}
if(br === undefined){
br = config.uploads.brotli
}
if(dfl === undefined){
dfl = config.uploads.deflate
}
if(file.length > config.uploads.max_filename){
return cb('upload filename too long')
} else if(obj.data.length > config.uploads.max_filesize){
return cb('upload size too large')
} else if(typeof obj.data !== 'string'){
return cb('invalid upload format')
} else {
let is_valid = false
for (let i in mtype) {
if(i === obj.ctype && mtype[i].indexOf(ext) !== -1){
is_valid = true;
fs.mkdir(base + dest, {recursive: config.uploads.recursive}, function(err){
if(err){return cb(err)}
if(gz){
obj.path += config.compression.gzip.ext;
obj.data = zlib.gzipSync(obj.data, config.compression.gzip.settings);
} else if(br){
obj.path += config.compression.brotli.ext;
obj.data = zlib.brotliCompressSync(obj.data, config.compression.brotli.settings);
} else if(dfl){
obj.path += config.compression.deflate.ext;
obj.data = zlib.deflateSync(obj.data, config.compression.deflate.settings);
}
fs.writeFile(base + obj.path, obj.data, function(err){
if(err){return cb(err)}
cb(false, file + ' upload success')
})
});
break;
}
}
if(!is_valid){
return cb('invalid file type')
}
}
} catch (err) {
cb(err)
}
},
fetch: function(obj, cb, tm){
tm = tm || config.fetch.timeout;
let options = utils.set_cert(),
data;
options = Object.assign(options, config.server);
const client = http2.connect(obj['url'], options);
delete obj.url;
if(obj.body){
data = obj.body;
delete obj.body;
}
let stream = client.request(obj),
body = '',
timeout = setTimeout(function(){
cb('connection timeout error')
stream.close();
}, tm),
headers;
stream.setEncoding('utf8');
stream.on('response', function(head){
headers = head;
});
stream.on('data', function(chunk){
body += chunk;
});
stream.on('end', function(){
let result = {
headers: headers,
text: body,
buffer: Buffer.from(body)
}
try {
clearTimeout(timeout)
let ctype = headers['content-type'].split(';')[0];
if(ctype === 'application/json'){
result.json = JSON.parse(body);
} else if(ctype === 'multipart/form-data'){
let b = utils.Boundary_parse(body);
b = utils.multiPart_parse(body, 'multipart/form-body; boundary=' + b);
result.text = b;
} else if(ctype === 'application/x-www-form-urlencoded'){
result.text = JSON.stringify(qs.parse(body));
}
result.statusText = 'ok'
} catch (err) {
result.statusText = 'not ok'
} finally {
cb(false, result)
stream.close();
}
});
if(data){
stream.end(data, 'utf8');
} else {
stream.end();
}
},
store_fetch: function(method, data, sel, cb){
if(typeof data === 'function'){
cb = data;
data = {}
}
let head = {
'url': config.cache.url,
':method': 'POST',
':path': '/',
'Content-Type': 'application/json',
'body': JSON.stringify({
method: 'store_' + method,
src: sel,
data: data
})
}
if(config.cache.authtoken.enabled){
head[config.cache.authtoken.header] = config.cache.authtoken.token;
}
utils.fetch(head, function(err,res){
if(err){return cb(err)}
cb(false, res.json)
})
},
set_cert: function(){
let cert_arr = ['cert', 'key', 'pfx', 'ca'];
let obj = {};
for (let i = 0; i < cert_arr.length; i++) {
if(config.ssl[cert_arr[i]] && config.ssl[cert_arr[i]] !== ''){
obj[cert_arr[i]] = fs.readFileSync(cwd + config.ssl[cert_arr[i]]);
if(cert_arr[i] === 'ca'){
obj.ca = [obj.ca]
}
}
}
return obj
},
set_methods: function(mth, evts, evt, router, server){
for (let i = 0; i < mth.length; i++) {
evts[mth[i]] = new evt();
router[mth[i]] = function(dest_url,stream,headers,flags){
server.dest_arr.push({'method': mth[i], path: dest_url})
return evts[mth[i]].on(dest_url,stream,headers,flags);
}
}
},
set_engine: function(engine, stream, config){
for (let i = 0; i < engine.engines.length; i++) {
if(engine[engine.engines[i]].enabled){
process.env.template_engine = engine.engines[i];
return require('../adapters/adapter')(stream, config);
}
}
},
uuid: function() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, function(c){
return (c ^ crypto.randomBytes(1)[0] & 15 >> c / 4).toString(16)
});
},
cc: function(x,y,z,err){
if(config.verbose){
if(z){
x[1] = x[1] + ' [push]'
}
console.log('\x1b[92m[\x1b[94msicarii\x1b[92m:\x1b[94m'+x[0]+'\x1b[92m] \x1b['+y+'m'+ x[1] +' \x1b[0m');
if(err){
console.error(err)
}
}
return
},
qs: function(data, i, e){
return qs.stringify(data, i, e)
},
cookie_encode: function(key, val, obj){
delete obj.Signed;
let new_obj = {};
new_obj[key] = val;
new_obj = Object.assign(new_obj, obj);
new_obj = qs.stringify(new_obj,';');
return new_obj + ';';
},
cookie_sign: function(key, val, obj){
val = crypt.hmac.sign(val, config.cookie_parser.sig.hmac);
key = [key, config.cookie_parser.sig.suffix].join('_')
return utils.cookie_encode(key, val, obj);
},
cookie_decode: function(obj){
try {
if(obj){
obj = JSON.parse(JSON.stringify(qs.parse(obj, '; ')));
}
return obj;
} catch (err) {
return null
}
},
cookie_verify: function(name, obj){
try {
obj = utils.cookie_decode(obj)
for (let i in obj) {
if(i.slice(0, -(config.cookie_parser.sig.suffix.length + 1)) === name){
let hash = crypt.hmac.sign(obj[name], config.cookie_parser.sig.hmac)
return obj[i] === hash;
}
}
return false;
} catch (err) {
return false
}
},
trim_arr: function(arr){
for (let i = 0; i < arr.length; i++){
arr[i] = arr[i].trim();
}
return arr
},
qsJSON: function(qs) {
let pairs = qs.split('&'),
res = {};
pairs.forEach(function(p) {
let pair = p.split('='),
key = pair[0],
value = decodeURIComponent(pair[1] || '');
if (res[key]) {
if (Object.prototype.toString.call(res[key]) === '[object Array]') {
res[key].push(value);
} else {
res[key] = [res[key], value];
}
} else {
res[key] = value;
}
});
res = JSON.parse(JSON.stringify(res));
return res;
},
etag: function(digest, data, encode){
return crypto.createHash(digest).update(data).digest(encode);
},
digest: function(prefix, digest, data, encode){
let hash = crypto.createHash(digest).update(data).digest(encode);
return [prefix, hash].join('=');
},
cache_stream: function(url, obj, cb){
if(!config[obj.src].cache.enabled){
return cb(false, {code: 200})
}
let options = utils.set_cert();
options = Object.assign(options, config.cache.server);
const client = http2.connect(url, options);
if(typeof obj !== 'string'){
obj = JSON.stringify(obj);
}
let head = {
':method': 'POST',
':path': '/',
'Content-Type': 'application/json'
}
if(config.cache.authtoken.enabled){
head[config.cache.authtoken.header] = config.cache.authtoken.token;
}
let stream = client.request(head),
timeout = setTimeout(function(){
cb('connection timeout error')
stream.close();
}, config.fetch.timeout),
body = '';
stream.setEncoding('utf8');
stream.on('data', function(chunk){
body += chunk;
});
stream.on('end', function(){
try {
clearTimeout(timeout)
let data = JSON.parse(body);
cb(false, data)
} catch (err) {
cb(err)
} finally {
stream.close();
}
});
stream.end(obj, 'utf8');
},
compress_ext: function(file){
if(config.compression.gzip.enabled && config.compression.gzip.prezipped){
file += config.compression.gzip.ext
} else if(config.compression.brotli.enabled && config.compression.brotli.prezipped){
file += config.compression.brotli.ext
} else if(config.compression.deflate.enabled && config.compression.deflate.prezipped){
file += config.compression.deflate.ext
}
return file;
},
render_sort: function($this, content, url, cb){
content = Buffer.from(content);
$this.headers['Content-Type'] = 'text/html; charset=utf-8';
if(config.compression.gzip.enabled){
content = zlib.gzipSync(content, config.compression.gzip.settings);
$this.headers['Content-Encoding'] = 'gzip';
} else if(config.compression.brotli.enabled){
content = zlib.brotliCompressSync(content, config.compression.brotli.settings);
$this.headers['Content-Encoding'] = 'br';
} else if(config.compression.deflate.enabled){
content = zlib.deflateSync(content, config.compression.deflate.settings);
$this.headers['Content-Encoding'] = 'deflate';
}
$this.headers['Content-Length'] = Buffer.byteLength(content);
if(config.render.etag.enabled){
$this.headers.Etag = utils.etag(
config.render.etag.digest,
content,
config.render.etag.encode
)
}
$this.headers = Object.assign(config.render.headers, $this.headers);
if(config.render.cache.enabled){
let cache_obj = {
src: 'render',
method: 'add_cache',
data: {
url: url,
data: content,
headers: $this.headers
}
}
utils.cache_stream(config.cache.url, cache_obj, function(err,res){
if(err){return console.error(err)}
})
}
$this.headers[':status'] = 200;
$this.respond($this.headers);
$this.end(content, 'utf-8');
utils.cc(['GET', url + ' 200 [file]'],92);
if(!cb){
return this;
}
cb(false);
},
compress_sort: function(headers, content){
if(config.compression.gzip.enabled){
if(!config.compression.gzip.prezipped){
content = zlib.gzipSync(content, config.compression.gzip.settings);
}
headers['Content-Encoding'] = 'gzip';
} else if(config.compression.brotli.enabled){
if(!config.compression.brotli.prezipped){
content = zlib.brotliCompressSync(content, config.compression.brotli.settings);
}
headers['Content-Encoding'] = 'br';
} else if(config.compression.deflate.enabled){
if(!config.compression.deflate.prezipped){
content = zlib.deflateSync(content, config.compression.deflate.settings);
}
headers['Content-Encoding'] = 'deflate';
}
return {
content: content,
headers:headers
}
},
pre_cache_init: function(){
let arr = ['static', 'render']
for (let x = 0; x < arr.length; x++) {
for (let i = 0; i < pre_cache[arr[x]].length; i++) {
utils.pre_cache_stream(arr[x], pre_cache[arr[x]][i].url, pre_cache[arr[x]][i].ctype)
}
}
},
pre_cache_stream: function(src, url, ctype){
try {
let file = utils.compress_ext('.' + config[src].path + url),
headers = {}
fs.readFile(file, function(err, content) {
if(err){console.error(err)}
headers['Content-Type'] = ctype;
let srt = utils.compress_sort(headers, content);
headers = srt.headers;
content = srt.content;
headers['Content-Length'] = Buffer.byteLength(content);
if(config[src].etag.enabled){
headers.Etag = utils.etag(
config[src].etag.digest,
content,
config[src].etag.encode,
)
}
headers = Object.assign(headers, config[src].headers)
let obj = {
src: src,
method: 'add_cache',
data: {
url: url,
data: content,
headers: headers
}
}
utils.cache_stream(config.cache.url, obj, function(err,res){
if(err){return console.error(err)}
})
});
} catch (err) {
return console.error(err)
}
},
serve: function(src, url, ctype, stream, is_push){
let obj = {
src: src,
method: 'get_cache',
data: {
url: url
}
}
utils.cache_stream(config.cache.url, obj, function(err,cached){
if(err){
cached = {code: 500};
}
try {
if(cached.code === 200 && cached.data && cached.data.data && cached.data.headers){
let res = cached.data.data;
if(typeof res === 'object' && res.data){
res = Buffer.from(res.data)
}
obj = {':status': 200};
obj = Object.assign(obj, cached.data.headers);
stream.respond(obj);
stream.end(res);
utils.cc(['GET', url + ' 200 [cache]'],92,is_push);
return;
}
let file = utils.compress_ext('.' + config[src].path + url);
fs.readFile(file, function(err, content) {
if(err){return utils.err(stream, 'GET', url, 404, 'Not Found', err)}
stream.headers['Content-Type'] = ctype;
let sorted = utils.compress_sort(stream.headers, content);
stream.headers = sorted.headers;
content = sorted.content;
stream.headers['Content-Length'] = Buffer.byteLength(content);
if(config[src].etag.enabled){
stream.headers.Etag = utils.etag(
config[src].etag.digest,
content,
config[src].etag.encode,
)
}
stream.headers = Object.assign(stream.headers, config[src].headers);
if(config[src].cache.enabled){
obj = {
src: src,
method: 'add_cache',
data: {
url: url,
data: content,
headers: stream.headers
}
}
utils.cache_stream(config.cache.url, obj, function(err,res){
if(err){return console.error(err)}
})
}
if(!stream.destroyed){
stream.headers[':status'] = 200;
stream.respond(stream.headers);
stream.end(content);
utils.cc(['GET', url + ' 200 [file]'],92, is_push);
}
});
} catch (err) {
return utils.err(stream, 'GET', url, 500, 'Bad request', err)
}
})
},
serve_static: function(url, ext, stream){
if(config.static.blocked.indexOf(url) !== -1){
return utils.err(stream, 'GET', url, 403, 'Forbidden')
}
let ctype = null,
found = false;
for (let i in config.mimetypes) {
if(config.mimetypes[i].indexOf(ext) !== -1) {
ctype = i;
utils.serve(process.env.static_path, url, ctype, stream);
found = true;
break;
}
}
if(!found){
return utils.err(stream, 'GET', url, 415, 'Unsupported Media Type')
}
},
push_stream: function(stream, src, ctype){
if(stream.pushAllowed){
stream.pushStream({':path': src}, function(err, pushStream){
if(err){return console.err(err)};
pushStream.on('error', function(err){
pushStream.end();
})
pushStream.headers = {}
utils.serve(process.env.static_path, src, ctype, pushStream, true);
})
}
},
err: function(stream, method, url, code, msg, error){
utils.cc([method, url + ' '+ code],91, error);
if(config.dev && error){
console.error(error)
}
if(!stream.destroyed){
stream.headers[':status'] = code;
stream.json({code: code, error: msg}, 'utf-8');
}
if(config.logs.error.enabled){
process.emit('message', {type: 'log_error', data: [Date.now(), method, url, code, msg].join('::::')})
}
},
cache_res: function(stream, code, msg){
stream.headers[':status'] = code
stream.respond(stream.headers);
return stream.end(JSON.stringify({code: code, data: msg}, 'utf8'));
},
h_parse: function(header) {
let headerFields = {},
matchResult = header.match(/^.*name="([^"]*)"$/);
if (matchResult){
headerFields.name = matchResult[1];
}
return headerFields;
},
is_bot: function(ua, arr){
let x = false;
try {
for (let i = 0; i < arr.length; i++) {
if(ua.includes(arr[i])){
x = true;
break;
}
}
return x;
} catch (err) {
console.log(err);
return x;
}
},
stream_check: function(router, ip, headers){
let in_valid = false;
if(config.bot.block.enabled){
let ua = headers['user-agent'],
arr = config.bot.block.items;
if(utils.is_bot(ua, arr)){
in_valid = config.bot.block.msg;
}
}
if(config.blacklist.enabled){
if(ip_config.blacklist.indexOf(ip) !== -1){
router.emit('auth_error', {type: 'blacklist', ip: ip});
in_valid = config.blacklist.msg;
}
}
if(config.whitelist.enabled && !in_valid){
if(ip_config.whitelist.indexOf(ip) === -1){
router.emit('auth_error', {type: 'whitelist', ip: ip});
in_valid = config.whitelist.msg;
}
}
if(config.authtoken.enabled && !in_valid){
router.emit('auth_error', {type: 'authtoken', ip: ip});
if(headers[config.authtoken.header] !== config.authtoken.token){
in_valid = config.authtoken.msg
}
}
return in_valid;
},
stream_body: function(stream, body, ctype){
let is_json = true,
is_buff = true;
stream.body = {};
if(ctype === 'multipart/form-data'){
let b = utils.Boundary_parse(body);
body = utils.multiPart_parse(body, 'multipart/form-body; boundary=' + b);
stream.body.text = body;
} else if(ctype === 'application/x-www-form-urlencoded'){
stream.body.text = JSON.stringify(qs.parse(body));
} else if(ctype === 'application/json'){
stream.body.text = body;
} else {
is_json = false;
stream.body.text = body;
}
if(is_json){
stream.body.json = JSON.parse(stream.body.text);
}
if(is_buff){
stream.body.buffer = Buffer.from(stream.body.text);
}
return stream;
},
stream_push: function(stream, headers, cpath){
let accept = headers['accept'].split(','),
accepted = config.push_handler.accept;
for (let j = 0; j < accepted.length; j++) {
if(accept.indexOf(accepted[j]) !== -1){
for (let i = 0; i < push_config.length; i++) {
if(push_config[i].url === cpath.pathname){
if(!push_config[i].items){
stream.pushStatic(push_config[i].path, push_config[i].ctype);
} else {
stream.pushStatic(push_config[i].items);
}
break;
}
}
break;
}
}
},
multiPart_parse: function(body, contentType) {
try {
let m = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
return 'Bad content-type header, no multipart boundary'
}
let boundary = m[1] || m[2],
isRaw = typeof(body) !== 'string',
s, fname;
boundary = '\r\n--' + boundary;
if (isRaw) {
s = String.fromCharCode.apply(null, new Uint8Array(body));
} else {
s = body;
}
s = '\r\n' + s;
let parts = s.split(new RegExp(boundary)),
partsByName = {},
headerFields;
for (var i = 1; i < parts.length - 1; i++) {
let subparts = parts[i].split('\r\n\r\n'),
headers = subparts[0].split('\r\n');
for (var j = 1; j < headers.length; j++) {
headerFields = utils.h_parse(headers[j]);
if (headerFields.name) {
fname = headerFields.name;
}
headerFields = null;
}
partsByName[fname] = isRaw ? Buffer.from(subparts[1]).buffer : subparts[1];
}
return JSON.stringify(partsByName);
} catch (err) {
return null
}
},
Boundary_parse:function(body) {
let x = body.split('Content-Disposition: form-data;')[0];
return x.trim().slice(2);
},
path_exists: function(check_exists, method, dest_url){
let path_exists = false;
for (let i = 0; i < check_exists.length; i++) {
if(check_exists[i].method === method && check_exists[i].path === dest_url){
path_exists = true;
break;
}
}
return path_exists;
},
requireUncached: function(src) {
delete require.cache[require.resolve(src)];
return require(src);
},
nocache: function(dest, data, cb){
try {
cb(false, require(dest)(data));
delete require.cache[require.resolve(dest)];
} catch (err) {
cb(err);
}
},
sort_ip: function(stream, headers){
let ip;
if(config.cache.proxy){
ip = headers['x-forwarded-for'] || stream.session.socket.remoteAddress;
} else {
ip = stream.session.socket.remoteAddress;
}
return ip;
},
add_ip: function(addr, i){
let dest = cwd + config.ip_config + '.json';
fs.readFile(dest, function(err,res){
if(err){return console.error(err)}
res = JSON.parse(res);
if(typeof addr === 'string'){
res[i].push(addr);
} else {
for (let x = 0; x < addr.length; i++) {
res[i].push(addr[x]);
}
}
fs.writeFile(dest, JSON.stringify(res), function(err){
if(err){return console.error(err)}
process.send({type: i})
})
})
},
store_add: function(src, obj, x, is_unshift){
try {
let add_items = x[src],
exists = false;
obj.date = Date.now();
if(config[src] && config[src].maxage){
obj.date = obj.date + config[src].maxage
}
for (let i = 0; i < add_items.length; i++) {
if(add_items[i].id === obj.id){
exists = true;
x[src][i] = obj;
}
}
if(exists){
return {success: true, msg: src +' updated'};
} else {
if(is_unshift){
x[src].unshift(obj);
} else {
x[src].push(obj);
}
return {success: true, msg: src +' added'};
}
} catch (err) {
return {success: false, msg: 'unable to add '+ src};
}
},
store_find: function(src, obj, x){
try {
let find_items = x[src],
data = {
success: true,
data: null
},
item = Object.keys(obj)[0];
for (let i = 0; i < find_items.length; i++) {
if(find_items[i][item] === obj[item]){
if(config[src] && config[src].maxage){
if(!find_items[i].date || find_items[i].date < Date.now()){
x.del_cache_index(src, {index: i});
} else {
data.data = find_items[i];
}
} else {
data.data = find_items[i];
}
}
}
return data;
} catch (err) {
return {success: false, msg: src+ ' find error'};
}
},
store_first: function(src, x, cnt){
try {
let first_items = x[src],
data = {success: true,data: null}
if(!cnt){
data.data = first_items[0]
} else {
data.data = first_items.slice(0, cnt)
}
return data;
} catch (err) {
return {success: false, msg: src+ ' find error'};
}
},
store_last: function(src, x, cnt){
try {
let last_items = x[src],
data = {success: true,data: null},
len = last_items.length;
if(!cnt){
cnt = 1
}
data.data = last_items.slice(len - cnt)
return data;
} catch (err) {
return {success: false, msg: src+ ' find error'};
}
},
store_assign: function(src, arr, x){
let msg = {success: false, msg: src+ ' assign error'};
try {
let assign_items = x[src],
item = Object.keys(arr[0])[0],
exists = false;
for (let i = 0; i < assign_items.length; i++) {
if(assign_items[i][item] === arr[0][item]){
x[src][i] = Object.assign(assign_items[i], arr[1]);
exists = true;
break;
}
}
if(exists){
msg.success = true;
msg.msg = src+ ' assign success';
}
return msg
} catch (err) {
return msg;
}
},
store_each: function(src, obj, x){
let msg = {success: false, msg: src+ ' each error'};
try {
let each_items = x[src];
for (let i = 0; i < each_items.length; i++) {
each_items[i] = Object.assign(each_items[i], obj);
}
msg.success = true;
msg.msg = src+ ' each success';
return msg
} catch (err) {
return msg;
}
},
store_omit: function(src, arr, x){
let msg = {success: false, msg: src+ ' omit error'};
try {
let omit_items = x[src];
for (let i = 0; i < omit_items.length; i++) {
for (let j = 0; j < arr.length; j++) {
delete omit_items[i][arr[j]]
}
}
msg.success = true;
msg.msg = src+ ' omit success';
return msg
} catch (err) {
return msg;
}
},
store_filter: function(src, obj, x){
try {
let filter_items = x[src],
item = Object.keys(obj)[0],
data = {
success: true,
data: null
}
data.data = filter_items.filter(function(i){
return i[item] !== obj[item]
})
return data;
} catch (err) {
return {success: false, msg: src+ ' filter error'};
}
},
store_compare: function(src, obj, x, sel){
try {
let compare_items = x[src],
item = Object.keys(obj)[0],
data = {
success: true,
data: null
}
if(sel === 'gt'){
data.data = compare_items.filter(function(i){
return i[item] > obj[item]
})
} else if(sel === 'lt'){
data.data = compare_items.filter(function(i){
return i[item] < obj[item]
})
} else if(sel === 'gte'){
data.data = compare_items.filter(function(i){
return i[item] >= obj[item]
})
} else if(sel === 'lte'){
data.data = compare_items.filter(function(i){
return i[item] <= obj[item]
})
}
return data;
} catch (err) {
return {success: false, msg: src+ ' filter '+ sel +' error'};
}
},
store_findIndex: function(src, obj, x){
try {
let idx_items = x[src],
item = Object.keys(obj)[0],
data = {
success: true,
data: null
}
data.data = idx_items.findIndex(function(i){
return i[item] === obj[item]
})
return data;
} catch (err) {
return {success: false, msg: src+ ' filter error'};
}
},
store_delete: function(src, obj, x){
try {
let del_items = x[src],
item = Object.keys(obj)[0],
data = {
success: true,
msg: src +' not found'
}
for (let i = 0; i < del_items.length; i++) {
if(del_items[i][item] === obj[item]){
x[src].splice(i, 1);
data.msg = src +' deleted'
}
}
return data;
} catch (err) {
return {success: false, msg: src+ ' delete error'};
}
},
store_chunk: function(src, arr, x){
try {
let data = {
success: true,
data: utils.chunk(x[src], arr[0])
}
if(arr[1]){
data.data = data.data.slice(0, arr[1])
}
return data;
} catch (err) {
return {success: false, msg: src+ ' chunk error'};
}
},
store_import: function(src, dest, x, cb){
let msg = {
success: true,
msg: src +' import success'
}
try {
let data = JSON.parse(fs.readFileSync(cwd + dest, 'utf8'));
x[src] = data;
} catch (err){
msg.success = false;
msg.msg = src +' import failed';
} finally{
return msg;
}
},
store_export: function(src, dest, x){
let msg = {
success: true,
msg: src +' export success'
},
data = JSON.stringify(x[src]);
fs.writeFile(cwd + dest, data, function(err){
if(err){return console.error(err)}
});
return msg;
},
store_sort: function(src, obj, x){
try {
let sort_items = x[src],
item = Object.keys(obj)[0],
data = {
success: true,
data: null
}
data.data = utils.sortBy(sort_items, obj.key)
if(obj.count){
data.data = data.data.slice(0, obj.count)
}
return data;
} catch (err) {
return {success: false, msg: src+ ' sort error'};
}
},
store_concat: function(src, arr, x){
try {
x[src] = x[src].concat(arr);
return {success: true, msg: src+ ' concat success'};
} catch (err) {
return {success: false, msg: src+ ' concat error'};
}
},
sortKey: function(key) {
return function (a, b) {
return a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0;
}
},
sortBy: function(arr,key){
return arr.concat().sort(utils.sortKey(key))
},
chunk: function(input, size){
return input.reduce(function(arr, item, idx){
return idx % size === 0
? [...arr, [item]]
: [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
}, []);
},
path: function(i){
try {
return path.parse(i);
} catch (err) {
return null;
}
},
url: function(i){
try {
return JSON.parse(JSON.stringify(url.parse(i,{parseQueryString:true})));
} catch (err) {
return null;
}
},
dns: {
get: function(dest, obj, cb){
let getcnf = {family:0, all: false};
if(typeof obj === 'object'){
getcnf = Object.assign(getcnf, obj)
} else {
cb = obj;
}
dns.lookup(dest, getcnf, function(err, address, family){
if(err){return cb(err)}
cb(false, {
address: address,
family: family
})
});
},
getService: function(dest, port, cb){
try { // nodejs bug ~ remove it if they 'ever' fix it
// dest validation should be handled within lookupService scope
dns.lookupService(dest, port, function(err, hostname, service){
if(err){return cb(err)}
cb(false, {
hostname: hostname,
service: service
})
})
} catch (err) {
cb(err)
}
},
reverse: function(dest, cb){
try { // nodejs bug 2 ~ remove it if they 'ever' fix it
// dest validation should be handled within reverse scope
dns.reverse(dest, function(err,res){
if(err){return cb(err)}
cb(false,res)
})
} catch (err) {
cb(err)
}
}
},
encode: function(data, from, to){
try {
if(['hex','base64','utf8'].indexOf(from) !== -1){
data = Buffer.from(data, from)
} else {
data = Buffer.from(data)
}
if(!to){
return data
}
if(['hex','base64','utf8'].indexOf(to) !== -1){
data = data.toString(to)
} else {
data = new global[to](data)
}
return data
} catch (err) {
return null
}
}
}
module.exports = utils;