UNPKG

tim

Version:

Tradle's P2P OTR-based chat with plaintext and structured messages, and the ability to externalize messages onto a blockchain (currently bitcoin's)

158 lines (135 loc) 3.85 kB
var debug = require('debug')('msgDB') var typeforce = require('typeforce') // var lexint = require('lexicographic-integer') var collect = require('stream-collector') var levelup = require('levelup') var Q = require('q') var pump = require('pump') var map = require('map-stream') // var levelQuery = require('level-queryengine') // var jsonQueryEngine = require('jsonquery-engine') var safe = require('safecb') var constants = require('@tradle/constants') // var TxData = require('tradle-tx-data').TxData var levelErrs = require('level-errors') var LiveStream = require('level-live-stream') var CUR_HASH = constants.CUR_HASH var ROOT_HASH = constants.ROOT_HASH var TYPE = constants.TYPE var TYPES = constants.TYPES var lb = require('logbase') var Entry = lb.Entry var LogBase = lb.Simple var EventType = require('./eventType') var rebuf = require('./rebufEncoding') var DEBUG = require('./debug') var utils = require('./utils') var Errors = require('./errors') var SEPARATOR = '!' var now = utils.now var ENTRY_TIMEOUT = DEBUG ? false : 5000 module.exports = function createMsgDB (path, options) { typeforce('String', path) typeforce({ leveldown: 'Function', log: 'Log', keeper: 'Object' }, options) var keeper = options.keeper var db = levelup(path, { db: options.leveldown, valueEncoding: rebuf }) db = LogBase({ db: db, log: options.log, process: processEntry, timeout: typeof options.timeout === 'undefined' ? ENTRY_TIMEOUT : options.timeout, autostart: options.autostart }) var topics = [ EventType.misc.forget, EventType.msg.new, EventType.msg.edit, EventType.msg.receivedValid, EventType.msg.sendSuccess, ] LiveStream.install(db) var createReadStream = db.createReadStream db.createReadStream = function (type) { return createReadStream.call(db, { lte: type + SEPARATOR + '\xff', gte: type + SEPARATOR, keys: false }) .pipe(map(function (val, cb) { cb(null, val.meta) })) } db.setMaxListeners(0) return db function myDebug () { var args = [].slice.call(arguments) args.unshift(db.name) return debug.apply(null, args) } function processEntry (entry, cb) { var eType = entry.get('type') if (topics.indexOf(eType) === -1) return cb() entry = entry.clone() if (eType === EventType.misc.forget) { return forget(entry, cb) } const type = entry.get(TYPE) if (!type) return cb() var key = type + SEPARATOR + entry.get(ROOT_HASH) return db.get(key, function (err, state) { if (!db.isOpen()) return cb() if (err && !err.notFound) throw err // should never happen var newEntry = state ? utils.updateEntry(state, entry) : entry var from = newEntry.get('from') from = from && from[ROOT_HASH] var to = newEntry.get('to') to = to && to[ROOT_HASH] var batch = [{ type: 'put', key: key, value: { // data needed for lookupObject meta: utils.pick(entry.toJSON(), ['txType', 'txData', 'addressesFrom', 'addressesTo']), participants: [ from, to ] } }] cb(batch) }) } function forget (entry, cb) { var otherGuyRootHash = entry.get('who') var togo = 3 var stream = db.rawReadStream({ gt: '\x00' }) .pipe(map(function (data, cb) { if (data.value.participants.indexOf(otherGuyRootHash) !== -1) { cb(null, data) } else { cb() } })) collect(stream, function (err, results) { if (err) return cb(err) var batch = results.map(function (data) { return { key: data.key, type: 'del' } }) cb(batch) }) } } function errWithNotFound (cb) { return cb(new levelErrs.NotFoundError()) }