bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
131 lines (123 loc) • 4.32 kB
JavaScript
var fs = require('fs');
var mkdirp = require('mkdirp');
var path = require('path');
var lockfile = require('lockfile');
var crypto = require('crypto');
function PlainObject () {}
PlainObject.prototype = Object.create(null);
function newObj () { return new PlainObject }
var globalCache = newObj();
module.exports = function (coll_name, backend_options) {
backend_options || (backend_options = {});
if (typeof backend_options.hashKeys === 'undefined') backend_options.hashKeys = true;
if (!backend_options.path) throw new Error('must pass a json file path with backend_options.path');
function escapeBase64 (str) {
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
}
function hash (id) {
return backend_options.hashKeys ? escapeBase64(crypto.createHash('sha1').update(id).digest('base64')) : id.replace(/\./g, '')
}
var coll_path = coll_name;
if (backend_options.key_prefix && backend_options.key_prefix.length) {
coll_path += '.' + backend_options.key_prefix.map(hash).join('.')
}
return {
_getColl: function (mem) {
mem[coll_path] || (mem[coll_path] = {keys: [], values: newObj()});
return mem[coll_path];
},
_readFile: function (cb) {
if (globalCache[backend_options.path]) return cb(null, globalCache[backend_options.path]);
try {
var locked = lockfile.checkSync(backend_options.path + '.lock', backend_options);
if (locked) {
var err = new Error('db is locked by another process');
throw err;
}
lockfile.lockSync(backend_options.path + '.lock', backend_options);
var raw = fs.readFileSync(backend_options.path, {encoding: 'utf8'});
var mem = JSON.parse(raw);
}
catch (err) {
if (err && err.code === 'ENOENT') {
mkdirp.sync(path.dirname(backend_options.path));
globalCache[backend_options.path] = newObj();
return cb(null, globalCache[backend_options.path]);
}
else return cb(err);
}
globalCache[backend_options.path] = mem;
cb(null, globalCache[backend_options.path]);
},
_writeFile: function (mem, cb) {
try {
var raw = JSON.stringify(mem, null, 2);
fs.writeFileSync(backend_options.path, raw, {mode: parseInt('0600', 8)});
}
catch (e) {
return cb(e);
}
setImmediate(cb);
},
load: function (id, opts, cb) {
var self = this;
var idKey = hash(id);
self._readFile(function (err, mem) {
if (err) return cb(err);
var coll = self._getColl(mem);
cb(null, coll.values[idKey] || null);
});
},
save: function (id, obj, opts, cb) {
var self = this;
var idKey = hash(id);
self._readFile(function (err, mem) {
if (err) return cb(err);
var coll = self._getColl(mem);
coll.values[idKey] = obj;
if (!~coll.keys.indexOf(id)) coll.keys.push(id);
self._writeFile(mem, function (err) {
if (err) return cb(err);
cb(null, coll.values[idKey]);
});
});
},
destroy: function (id, opts, cb) {
var self = this;
var idKey = hash(id);
self._readFile(function (err, mem) {
if (err) return cb(err);
var coll = self._getColl(mem);
var obj = coll.values[idKey] || null;
if (obj) {
var idx = coll.keys.indexOf(id);
if (idx !== -1) coll.keys.splice(idx, 1);
delete coll.values[idKey];
self._writeFile(mem, function (err) {
if (err) return cb(err);
cb(null, obj);
});
}
else cb(null, null);
});
},
select: function (opts, cb) {
var self = this;
self._readFile(function (err, mem) {
if (err) return cb(err);
var coll = self._getColl(mem);
var keys = coll.keys.slice();
if (opts.reverse) keys.reverse();
var begin = opts.offset || 0;
var end = opts.limit ? begin + opts.limit : undefined;
if (begin || end) keys = keys.slice(begin, end);
var objs = keys.map(function (id) {
var key = hash(id);
return coll.values[key] || null;
});
cb(null, objs);
});
}
};
};
module.exports.globalCache = globalCache;