commit
721cb8fed1
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/* globals process */
|
||||||
|
|
||||||
|
var Config = require("./config");
|
||||||
|
var Fs = require("fs");
|
||||||
|
var Storage = require(Config.storage);
|
||||||
|
|
||||||
|
var args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (!args.length) {
|
||||||
|
console.log("Insufficient arguments!");
|
||||||
|
console.log("Pass a path to a database backup!");
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dump = Fs.readFileSync(args[0], 'utf-8');
|
||||||
|
|
||||||
|
var ready = function (store) {
|
||||||
|
var lock = 0;
|
||||||
|
dump.split(/\n/)
|
||||||
|
.filter(function (line) {
|
||||||
|
return line;
|
||||||
|
})
|
||||||
|
.forEach(function (line, i) {
|
||||||
|
lock++;
|
||||||
|
var parts;
|
||||||
|
|
||||||
|
var channel;
|
||||||
|
var msg;
|
||||||
|
|
||||||
|
line.replace(/^(.*?)\|(.*)$/, function (all, c, m) {
|
||||||
|
channel = c;
|
||||||
|
msg = m;
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!channel || !msg) {
|
||||||
|
console.log("BAD LINE on line %s", i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSON.parse(msg);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("BAD LINE on line %s", i);
|
||||||
|
console.log(msg);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
store.message(channel, msg, function () {
|
||||||
|
console.log(line);
|
||||||
|
lock--;
|
||||||
|
if (!lock) {
|
||||||
|
console.log("DONE");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Storage.create(Config, function (store) {
|
||||||
|
console.log("READY");
|
||||||
|
ready(store);
|
||||||
|
});
|
||||||
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
As the log statement says, this module does nothing to persist your data
|
|
||||||
across sessions. If your process crashes for any reason, all pads will die.
|
|
||||||
|
|
||||||
This might be useful if you want to debug other parts of the codebase, if
|
|
||||||
you want to test out cryptpad without installing mongodb locally, or if
|
|
||||||
you don't want to rely on a remote db like the one at mongolab.com.
|
|
||||||
|
|
||||||
Maybe you just like the idea of a forgetful pad? To use this module, edit
|
|
||||||
config.js to include a directive `storage: './storage/amnesia'
|
|
||||||
|
|
||||||
Enjoy!
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports.create = function(conf, cb){
|
|
||||||
console.log("Loading amnesiadb. This is a horrible idea in production,"+
|
|
||||||
" as data *will not* persist\n");
|
|
||||||
|
|
||||||
var db=[],
|
|
||||||
index=0;
|
|
||||||
|
|
||||||
if (conf.removeChannels) {
|
|
||||||
console.log("Server is set to remove channels %sms after the last remaining client leaves.", conf.channelRemovalTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb({
|
|
||||||
message: function(channelName, content, cb){
|
|
||||||
var val = {
|
|
||||||
id:index++,
|
|
||||||
chan: channelName,
|
|
||||||
msg: content,
|
|
||||||
time: new Date().getTime(),
|
|
||||||
};
|
|
||||||
db.push(val);
|
|
||||||
if (cb) { cb(); }
|
|
||||||
},
|
|
||||||
getMessages: function(channelName, handler, cb){
|
|
||||||
db.sort(function(a,b){
|
|
||||||
return a.id - b.id;
|
|
||||||
});
|
|
||||||
db.filter(function(val){
|
|
||||||
return val.chan === channelName;
|
|
||||||
}).forEach(function(doc){
|
|
||||||
handler(doc.msg);
|
|
||||||
});
|
|
||||||
if (cb) { cb(); }
|
|
||||||
},
|
|
||||||
removeChannel: function (channelName, cb) {
|
|
||||||
var err = false;
|
|
||||||
db = db.filter(function (msg) {
|
|
||||||
return msg.chan !== channelName;
|
|
||||||
});
|
|
||||||
cb(err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,22 +1,195 @@
|
|||||||
var Fs = require("fs");
|
var Fs = require("fs");
|
||||||
|
var Path = require("path");
|
||||||
|
var nThen = require("nthen");
|
||||||
|
|
||||||
var insert = function (env, channel, content, cb) {
|
var mkPath = function (env, channelId) {
|
||||||
|
return Path.join(env.root, channelId.slice(0, 2), channelId) + '.ndjson';
|
||||||
|
};
|
||||||
|
|
||||||
|
var readMessages = function (path, msgHandler, cb) {
|
||||||
|
var remainder = '';
|
||||||
|
var stream = Fs.createReadStream(path, 'utf8');
|
||||||
|
var complete = function (err) {
|
||||||
|
var _cb = cb;
|
||||||
|
cb = undefined;
|
||||||
|
if (_cb) { _cb(err); }
|
||||||
|
};
|
||||||
|
stream.on('data', function (chunk) {
|
||||||
|
var lines = chunk.split('\n');
|
||||||
|
lines[0] = remainder + lines[0];
|
||||||
|
remainder = lines.pop();
|
||||||
|
lines.forEach(msgHandler);
|
||||||
|
});
|
||||||
|
stream.on('end', function () {
|
||||||
|
msgHandler(remainder);
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
stream.on('error', function (e) { complete(e); });
|
||||||
};
|
};
|
||||||
|
|
||||||
var getMessages = function (env, channelName, msgHandler, cb) {
|
var checkPath = function (path, callback) {
|
||||||
|
Fs.stat(path, function (err, stats) {
|
||||||
|
if (!err) {
|
||||||
|
callback(undefined, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var dirPath = path.replace(/\/[^\/]*$/, '/');
|
||||||
|
Fs.mkdir(dirPath, function (err) {
|
||||||
|
if (err && err.code !== 'EEXIST') {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(undefined, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getChannel = function (env, id, callback) {
|
||||||
|
if (env.channels[id]) {
|
||||||
|
var chan = env.channels[id];
|
||||||
|
if (chan.whenLoaded) {
|
||||||
|
chan.whenLoaded.push(callback);
|
||||||
|
} else {
|
||||||
|
callback(undefined, chan);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var channel = env.channels[id] = {
|
||||||
|
atime: +new Date(),
|
||||||
|
messages: [],
|
||||||
|
writeStream: undefined,
|
||||||
|
whenLoaded: [ callback ],
|
||||||
|
onError: [ ]
|
||||||
|
};
|
||||||
|
var complete = function (err) {
|
||||||
|
var whenLoaded = channel.whenLoaded;
|
||||||
|
// no guarantee stream.on('error') will not cause this to be called multiple times
|
||||||
|
if (!whenLoaded) { return; }
|
||||||
|
channel.whenLoaded = undefined;
|
||||||
|
if (err) {
|
||||||
|
delete env.channels[id];
|
||||||
|
}
|
||||||
|
whenLoaded.forEach(function (wl) { wl(err, (err) ? undefined : channel); });
|
||||||
|
};
|
||||||
|
var path = mkPath(env, id);
|
||||||
|
var fileExists;
|
||||||
|
var errorState;
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
checkPath(path, waitFor(function (err, exists) {
|
||||||
|
if (err) {
|
||||||
|
errorState = true;
|
||||||
|
complete(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileExists = exists;
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
if (errorState) { return; }
|
||||||
|
if (!fileExists) { return; }
|
||||||
|
readMessages(path, function (msg) {
|
||||||
|
channel.messages.push(msg);
|
||||||
|
}, waitFor(function (err) {
|
||||||
|
if (err) {
|
||||||
|
errorState = true;
|
||||||
|
complete(err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
if (errorState) { return; }
|
||||||
|
var stream = channel.writeStream = Fs.createWriteStream(path, { flags: 'a' });
|
||||||
|
stream.on('open', waitFor());
|
||||||
|
stream.on('error', function (err) {
|
||||||
|
// this might be called after this nThen block closes.
|
||||||
|
if (channel.whenLoaded) {
|
||||||
|
complete(err);
|
||||||
|
} else {
|
||||||
|
channel.onError.forEach(function (handler) {
|
||||||
|
handler(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
if (errorState) { return; }
|
||||||
|
complete();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.create = function (conf, cb) {
|
var message = function (env, chanName, msg, cb) {
|
||||||
var env = {};
|
getChannel(env, chanName, function (err, chan) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var complete = function (err) {
|
||||||
|
var _cb = cb;
|
||||||
|
cb = undefined;
|
||||||
|
if (_cb) { _cb(err); }
|
||||||
|
};
|
||||||
|
chan.onError.push(complete);
|
||||||
|
chan.writeStream.write(msg + '\n', function () {
|
||||||
|
chan.onError.splice(chan.onError.indexOf(complete) - 1, 1);
|
||||||
|
if (!cb) { return; }
|
||||||
|
chan.messages.push(msg);
|
||||||
|
chan.atime = +new Date();
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getMessages = function (env, chanName, handler, cb) {
|
||||||
|
getChannel(env, chanName, function (err, chan) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
chan.messages
|
||||||
|
.forEach(function (message) {
|
||||||
|
if (!message) { return; }
|
||||||
|
handler(message);
|
||||||
|
});
|
||||||
|
} catch (err2) {
|
||||||
|
console.error(err2);
|
||||||
|
cb(err2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chan.atime = +new Date();
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeChannel = function (env, channelName, cb) {
|
||||||
|
var filename = Path.join(env.root, channelName.slice(0, 2), channelName + '.ndjson');
|
||||||
|
Fs.unlink(filename, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.create = function (conf, cb) {
|
||||||
|
var env = {
|
||||||
|
root: conf.filePath || './datastore',
|
||||||
|
channels: { },
|
||||||
|
};
|
||||||
|
console.log('storing data in ' + env.root);
|
||||||
|
Fs.mkdir(env.root, function (err) {
|
||||||
|
if (err && err.code !== 'EEXIST') {
|
||||||
|
// TODO: somehow return a nice error
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
cb({
|
cb({
|
||||||
message: function (channelName, content, cb) {
|
message: function (channelName, content, cb) {
|
||||||
insert(env, channelName, content, cb);
|
message(env, channelName, content, cb);
|
||||||
},
|
},
|
||||||
getMessages: function (channelName, msgHandler, cb) {
|
getMessages: function (channelName, msgHandler, cb) {
|
||||||
getMessages(env, channelName, msgHandler, cb);
|
getMessages(env, channelName, msgHandler, cb);
|
||||||
},
|
},
|
||||||
|
removeChannel: function (channelName, cb) {
|
||||||
|
removeChannel(env, channelName, function (err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
var kad=require("kad");
|
|
||||||
var levelup=require("levelup");
|
|
||||||
|
|
||||||
/*
|
|
||||||
THiS FILE IS NOT PRODUCTION READY
|
|
||||||
DON'T USE IT!
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports.create=function(conf,cb){
|
|
||||||
var dht= kad({
|
|
||||||
address:conf.kadAddress,
|
|
||||||
port:conf.kadPort,
|
|
||||||
storage:levelup(conf.kadStore),
|
|
||||||
seeds:conf.kadSeeds,
|
|
||||||
transport: kad.transports.UDP,
|
|
||||||
});
|
|
||||||
|
|
||||||
var getIndex=function(cName,f){
|
|
||||||
dht.get(cName+'=>index',function(e,out){
|
|
||||||
e && console.error(e) || f(Number(out));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
cb({
|
|
||||||
message:function(cName, content, cb){
|
|
||||||
getIndex(cName, function(index){
|
|
||||||
index+=1;
|
|
||||||
dht.put(cName+'=>index', ''+index,function(e){
|
|
||||||
e && console.error("ERROR updating index (%s): %s",index,e) ||
|
|
||||||
console.log("PUT SUCCESS: %s", cName+'=>index')
|
|
||||||
});
|
|
||||||
dht.put(cName+'=>'+index, content, function(e){
|
|
||||||
e && console.error("ERROR updating value at %s: %s",cName+'=>'+index,e)||
|
|
||||||
console.log("PUT SUCCESS: %s", cName+'=>index')
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getMessages: function(cName, cb){
|
|
||||||
getIndex(cName, function(index){
|
|
||||||
for(var i=index;i>=0;i--){
|
|
||||||
dht.get(cName+'=>'+i,function(e,out){
|
|
||||||
if(e) return console.error("DHT GET ERROR: %s",e);
|
|
||||||
console.log("GET SUCCESS: %s", cName+'=>index')
|
|
||||||
cb(out);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,73 +0,0 @@
|
|||||||
var Level = require("level");
|
|
||||||
var nThen = require('nthen');
|
|
||||||
|
|
||||||
var getIndex = function(db, cName, cb) {
|
|
||||||
db.get(cName+'=>index', function(e, out){
|
|
||||||
if (e) {
|
|
||||||
if (e.notFound) {
|
|
||||||
cb(-1);
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cb(parseInt(out));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var insert = function (db, channelName, content, cb) {
|
|
||||||
var index;
|
|
||||||
var doIt = function () {
|
|
||||||
db.locked = true;
|
|
||||||
nThen(function (waitFor) {
|
|
||||||
getIndex(db, channelName, waitFor(function (i) { index = i+1; }));
|
|
||||||
}).nThen(function (waitFor) {
|
|
||||||
db.put(channelName+'=>'+index, content, waitFor(function (e) { if (e) { throw e; } }));
|
|
||||||
}).nThen(function (waitFor) {
|
|
||||||
db.put(channelName+'=>index', ''+index, waitFor(function (e) { if (e) { throw e; } }));
|
|
||||||
}).nThen(function (waitFor) {
|
|
||||||
db.locked = false;
|
|
||||||
if (!db.queue.length) { return; }
|
|
||||||
db.queue.shift()();
|
|
||||||
}).nThen(cb);
|
|
||||||
};
|
|
||||||
if (db.locked) {
|
|
||||||
db.queue.push(doIt);
|
|
||||||
} else {
|
|
||||||
doIt();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var getMessages = function (db, channelName, msgHandler, cb) {
|
|
||||||
var index;
|
|
||||||
nThen(function (waitFor) {
|
|
||||||
getIndex(db, channelName, waitFor(function (i) {
|
|
||||||
index = i;
|
|
||||||
}));
|
|
||||||
}).nThen(function (waitFor) {
|
|
||||||
var again = function (i) {
|
|
||||||
db.get(channelName + '=>' + i, waitFor(function (e, out) {
|
|
||||||
if (e) { throw e; }
|
|
||||||
msgHandler(out);
|
|
||||||
if (i < index) { again(i+1); }
|
|
||||||
else if (cb) { cb(); }
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
if (index > -1) { again(0); }
|
|
||||||
else if (cb) { cb(); }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.create = function (conf, cb) {
|
|
||||||
var db = Level(conf.levelPath || './test.level.db');
|
|
||||||
db.locked = false;
|
|
||||||
db.queue = [];
|
|
||||||
cb({
|
|
||||||
message: function (channelName, content, cb) {
|
|
||||||
insert(db, channelName, content, cb);
|
|
||||||
},
|
|
||||||
getMessages: function (channelName, msgHandler, cb) {
|
|
||||||
getMessages(db, channelName, msgHandler, cb);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014 XWiki SAS
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
var MongoClient = require('mongodb').MongoClient;
|
|
||||||
|
|
||||||
var MONGO_URI = "mongodb://demo_user:demo_password@ds027769.mongolab.com:27769/demo_database";
|
|
||||||
var COLLECTION_NAME = 'cryptpad';
|
|
||||||
|
|
||||||
var insert = function (coll, channelName, content, cb) {
|
|
||||||
var val = {chan: channelName, msg:content, time: (new Date()).getTime()};
|
|
||||||
coll.insertOne(val, {}, function (err, r) {
|
|
||||||
console.log(r);
|
|
||||||
if (err || (r.insertedCount !== 1)) {
|
|
||||||
console.log('failed to insert ' + err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var getMessages = function (coll, channelName, cb) {
|
|
||||||
// find entries with a matching channelname
|
|
||||||
coll.find({chan:channelName})
|
|
||||||
// sort by _id, ascending
|
|
||||||
.sort( { _id: 1 } )
|
|
||||||
// iterate over entries
|
|
||||||
.forEach(function (doc) {
|
|
||||||
cb(doc.msg);
|
|
||||||
}, function (err) {
|
|
||||||
if (!err) { return; }
|
|
||||||
console.log('error ' + err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.create = function (conf, cb) {
|
|
||||||
MongoClient.connect(conf.mongoUri, function(err, db) {
|
|
||||||
var coll = db.collection(conf.mongoCollectionName);
|
|
||||||
if (err) { throw err; }
|
|
||||||
cb({
|
|
||||||
message: function (channelName, content, cb) {
|
|
||||||
insert(coll, channelName, content, cb);
|
|
||||||
},
|
|
||||||
getMessages: function (channelName, msgHandler) {
|
|
||||||
getMessages(coll, channelName, msgHandler);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,58 +0,0 @@
|
|||||||
var Knex = require("knex");
|
|
||||||
|
|
||||||
var getMessages = function (knex, channel, msgHandler, cb) {
|
|
||||||
return knex('messages')
|
|
||||||
.where({
|
|
||||||
channel: channel,
|
|
||||||
})
|
|
||||||
.select('*')
|
|
||||||
.then(function (rows) {
|
|
||||||
rows.forEach(function (row) {
|
|
||||||
msgHandler(row.content);
|
|
||||||
});
|
|
||||||
cb();
|
|
||||||
})
|
|
||||||
.catch(function (e) {
|
|
||||||
console.error(e);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var insert = function (knex, channel, content, cb) {
|
|
||||||
knex.table('messages').insert({
|
|
||||||
channel: channel,
|
|
||||||
content: content,
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.create = function (conf, cb) {
|
|
||||||
var knex = Knex({
|
|
||||||
dialect: 'sqlite3',
|
|
||||||
connection: conf.dbConnection,
|
|
||||||
useNullAsDefault: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
knex.schema.hasTable('messages').then(function (exists) {
|
|
||||||
if (exists) { return; }
|
|
||||||
|
|
||||||
return knex.schema.createTable('messages', function (table) {
|
|
||||||
table.increments('id');
|
|
||||||
table.string('content');
|
|
||||||
table.string('channel');
|
|
||||||
table.timestamps();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
cb({
|
|
||||||
message: function (channelName, content, cb) {
|
|
||||||
insert(knex, channelName, content, cb);
|
|
||||||
},
|
|
||||||
getMessages: function (channelName, msgHandler, cb) {
|
|
||||||
getMessages(knex, channelName, msgHandler, cb);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
Loading…
Reference in New Issue