parent
0209375865
commit
eb0f868f75
|
@ -47,6 +47,20 @@ DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) {
|
||||||
}, skipExpire);
|
}, skipExpire);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DocumentHandler.prototype.handleDelete = function (key, secret, response) {
|
||||||
|
this.store.remove(key, secret, function (ret) {
|
||||||
|
if (ret) {
|
||||||
|
winston.verbose('removed document', { key: key });
|
||||||
|
response.writeHead(200, { 'content-type': 'application/json' });
|
||||||
|
response.end(JSON.stringify({ message: "Removed document " + key }));
|
||||||
|
} else {
|
||||||
|
winston.warn('invalid secret or document not found', { key: key, secret: secret });
|
||||||
|
response.writeHead(404, { 'content-type': 'application/json' });
|
||||||
|
response.end(JSON.stringify({ message: 'Document not found.' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Handle adding a new Document
|
// Handle adding a new Document
|
||||||
DocumentHandler.prototype.handlePost = function (request, response) {
|
DocumentHandler.prototype.handlePost = function (request, response) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
@ -67,11 +81,11 @@ DocumentHandler.prototype.handlePost = function (request, response) {
|
||||||
}
|
}
|
||||||
// And then save if we should
|
// And then save if we should
|
||||||
_this.chooseKey(function (key) {
|
_this.chooseKey(function (key) {
|
||||||
_this.store.set(key, buffer, function (res) {
|
_this.store.set(key, buffer, function (res, secret) {
|
||||||
if (res) {
|
if (res) {
|
||||||
winston.verbose('added document', { key: key });
|
winston.verbose('added document', { key: key, secret: secret });
|
||||||
response.writeHead(200, { 'content-type': 'application/json' });
|
response.writeHead(200, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ key: key }));
|
response.end(JSON.stringify({ key: key, secret: secret }));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
winston.verbose('error adding document');
|
winston.verbose('error adding document');
|
||||||
|
|
|
@ -12,6 +12,11 @@ var FileDocumentStore = function(options) {
|
||||||
this.expire = options.expire;
|
this.expire = options.expire;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generate secret (10 chars)
|
||||||
|
FileDocumentStore.secret = function() {
|
||||||
|
return Math.random().toString(24).slice(-10);
|
||||||
|
};
|
||||||
|
|
||||||
// Generate md5 of a string
|
// Generate md5 of a string
|
||||||
FileDocumentStore.md5 = function(str) {
|
FileDocumentStore.md5 = function(str) {
|
||||||
var md5sum = crypto.createHash('md5');
|
var md5sum = crypto.createHash('md5');
|
||||||
|
@ -25,13 +30,17 @@ FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||||
try {
|
try {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
fs.mkdir(this.basePath, '700', function() {
|
fs.mkdir(this.basePath, '700', function() {
|
||||||
|
var sc = FileDocumentStore.secret();
|
||||||
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
||||||
|
var sfn = fn + "-" + sc;
|
||||||
fs.writeFile(fn, data, 'utf8', function(err) {
|
fs.writeFile(fn, data, 'utf8', function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(false);
|
callback(false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
callback(true);
|
fs.writeFile(sfn, "A validation file for " + fn, 'utf8', function (err) {
|
||||||
|
if (!err) callback(true, sc);
|
||||||
|
});
|
||||||
if (_this.expire && !skipExpire) {
|
if (_this.expire && !skipExpire) {
|
||||||
winston.warn('file store cannot set expirations on keys');
|
winston.warn('file store cannot set expirations on keys');
|
||||||
}
|
}
|
||||||
|
@ -43,6 +52,27 @@ FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FileDocumentStore.prototype.remove = function(str, key, callback) {
|
||||||
|
var file = FileDocumentStore.md5(str);
|
||||||
|
var kfn = key;
|
||||||
|
fs.exists(this.basePath + "/" + file + "-" + kfn, function(exists) {
|
||||||
|
if (exists) {
|
||||||
|
try {
|
||||||
|
fs.unlink(this.basePath + "/" + file, function() {
|
||||||
|
fs.unlink(this.basePath + "/" + file + "-" + kfn, function() {
|
||||||
|
callback(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Get data from a file from key
|
// Get data from a file from key
|
||||||
FileDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
FileDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
|
@ -8,7 +8,9 @@ var MemcachedDocumentStore = function(options) {
|
||||||
MemcachedDocumentStore.connect(options);
|
MemcachedDocumentStore.connect(options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
MemcachedDocumentStore.secret = function() {
|
||||||
|
return Math.random().toString(24).slice(-10);
|
||||||
|
};
|
||||||
// Create a connection
|
// Create a connection
|
||||||
MemcachedDocumentStore.connect = function(options) {
|
MemcachedDocumentStore.connect = function(options) {
|
||||||
var host = options.host || '127.0.0.1';
|
var host = options.host || '127.0.0.1';
|
||||||
|
@ -27,7 +29,17 @@ MemcachedDocumentStore.connect = function(options) {
|
||||||
MemcachedDocumentStore.prototype.set =
|
MemcachedDocumentStore.prototype.set =
|
||||||
function(key, data, callback, skipExpire) {
|
function(key, data, callback, skipExpire) {
|
||||||
MemcachedDocumentStore.client.set(key, data, function(err, reply) {
|
MemcachedDocumentStore.client.set(key, data, function(err, reply) {
|
||||||
err ? callback(false) : callback(true);
|
if (!err) {
|
||||||
|
var sc = MemcachedDocumentStore.secret();
|
||||||
|
MemcachedDocumentStore.client.set(key + "-" + sc, "", function(err, reply) {
|
||||||
|
if (!err) {
|
||||||
|
callback(true, sc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
}, skipExpire ? 0 : this.expire);
|
}, skipExpire ? 0 : this.expire);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,4 +54,21 @@ MemcachedDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MemcachedDocumentStore.prototype.remove = function(key, secret, callback) {
|
||||||
|
MemcachedDocumentStore.client.get(key + "-" + secret, function(err, reply) {
|
||||||
|
if (!err) {
|
||||||
|
MemcachedDocumentStore.client.del(key, function(err, reply) {
|
||||||
|
if (err) return callback(false);
|
||||||
|
MemcachedDocumentStore.client.del(key + "-" + secret, function(err, reply) {
|
||||||
|
if (err) return callback(false);
|
||||||
|
callback(reply);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = MemcachedDocumentStore;
|
module.exports = MemcachedDocumentStore;
|
||||||
|
|
|
@ -9,6 +9,7 @@ var winston = require('winston');
|
||||||
var PostgresDocumentStore = function (options) {
|
var PostgresDocumentStore = function (options) {
|
||||||
this.expireJS = options.expire;
|
this.expireJS = options.expire;
|
||||||
this.connectionUrl = process.env.DATABASE_URL || options.connectionUrl;
|
this.connectionUrl = process.env.DATABASE_URL || options.connectionUrl;
|
||||||
|
this.secret = () => Math.random().toString(24).slice(-10);
|
||||||
};
|
};
|
||||||
|
|
||||||
PostgresDocumentStore.prototype = {
|
PostgresDocumentStore.prototype = {
|
||||||
|
@ -19,16 +20,18 @@ PostgresDocumentStore.prototype = {
|
||||||
var that = this;
|
var that = this;
|
||||||
this.safeConnect(function (err, client, done) {
|
this.safeConnect(function (err, client, done) {
|
||||||
if (err) { return callback(false); }
|
if (err) { return callback(false); }
|
||||||
client.query('INSERT INTO entries (key, value, expiration) VALUES ($1, $2, $3)', [
|
var sc = that.secret();
|
||||||
|
client.query('INSERT INTO entries (key, value, expiration, secret) VALUES ($1, $2, $3, $4)', [
|
||||||
key,
|
key,
|
||||||
data,
|
data,
|
||||||
that.expireJS && !skipExpire ? that.expireJS + now : null
|
that.expireJS && !skipExpire ? that.expireJS + now : null,
|
||||||
|
sc
|
||||||
], function (err, result) {
|
], function (err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
winston.error('error persisting value to postgres', { error: err });
|
winston.error('error persisting value to postgres', { error: err });
|
||||||
return callback(false);
|
return callback(false);
|
||||||
}
|
}
|
||||||
callback(true);
|
callback(true, sc);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -62,6 +65,32 @@ PostgresDocumentStore.prototype = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
remove: function (key, secret, callback) {
|
||||||
|
var now = Math.floor(new Date().getTime() / 1000);
|
||||||
|
this.safeConnect(function (err, client, done) {
|
||||||
|
if (err) { return callback(false); }
|
||||||
|
client.query('SELECT id,value,expiration,secret from entries where KEY = $1 and (expiration IS NULL or expiration > $2)', [key, now], function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('error retrieving value from postgres', { error: err });
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
if (result.rows[0].secret == secret) {
|
||||||
|
client.query("DELETE FROM entries where KEY = $1", [key], function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
winston.error('error removing an item from postgres', { error: err });
|
||||||
|
return callback(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(true);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// A connection wrapper
|
// A connection wrapper
|
||||||
safeConnect: function (callback) {
|
safeConnect: function (callback) {
|
||||||
postgres.connect(this.connectionUrl, function (err, client, done) {
|
postgres.connect(this.connectionUrl, function (err, client, done) {
|
||||||
|
|
|
@ -43,6 +43,11 @@ RedisDocumentStore.connect = function(options) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generate secret (10 chars)
|
||||||
|
RedisDocumentStore.secret = function() {
|
||||||
|
return Math.random().toString(24).slice(-10);
|
||||||
|
};
|
||||||
|
|
||||||
// Save file in a key
|
// Save file in a key
|
||||||
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
@ -54,7 +59,11 @@ RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||||
if (!skipExpire) {
|
if (!skipExpire) {
|
||||||
_this.setExpiration(key);
|
_this.setExpiration(key);
|
||||||
}
|
}
|
||||||
callback(true);
|
var sc = RedisDocumentStore.secret();
|
||||||
|
RedisDocumentStore.client.set(key + '-' + sc, "", function(err, reply) {
|
||||||
|
if (err) return callback(false);
|
||||||
|
callback(true, sc);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -81,4 +90,21 @@ RedisDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RedisDocumentStore.prototype.remove = function(key, secret, callback) {
|
||||||
|
RedisDocumentStore.client.get(key + "-" + secret, function(err, reply) {
|
||||||
|
if (!err) {
|
||||||
|
RedisDocumentStore.client.del(key, function(err, reply) {
|
||||||
|
if (err) return callback(false);
|
||||||
|
RedisDocumentStore.client.del(key + "-" + secret, function(err, reply) {
|
||||||
|
if (err) return callback(false);
|
||||||
|
callback(reply);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = RedisDocumentStore;
|
module.exports = RedisDocumentStore;
|
||||||
|
|
|
@ -131,6 +131,9 @@ app.use(route(function(router) {
|
||||||
skipExpire
|
skipExpire
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
router.get('/documents/:id/remove/:secret', function(request, response, next) {
|
||||||
|
return documentHandler.handleDelete(request.params.id, request.params.secret, response);
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Otherwise, try to match static files
|
// Otherwise, try to match static files
|
||||||
|
|
Loading…
Reference in New Issue