Compare commits
1 Commits
6271da2ff3
...
17320ee1dd
Author | SHA1 | Date | |
---|---|---|---|
17320ee1dd |
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 7777,
|
"port": 7777,
|
||||||
"keyLength": 5,
|
"keyLength": 10,
|
||||||
"keySpace": "abcdefghijklmnopqrstuvwxyz0123456789",
|
"maxLength": 400000,
|
||||||
"staticMaxAge": 86400,
|
"staticMaxAge": 86400,
|
||||||
|
"recompressStaticAssets": true,
|
||||||
"logging": [
|
"logging": [
|
||||||
{
|
{
|
||||||
"level": "verbose",
|
"level": "verbose",
|
||||||
|
@ -1,102 +1,119 @@
|
|||||||
var Busboy = require('busboy');
|
var Busboy = require('busboy');
|
||||||
var Winston = require('winston');
|
var Winston = require('winston');
|
||||||
|
|
||||||
// For handling serving stored documents.
|
// For handling serving stored documents
|
||||||
|
|
||||||
var DocumentHandler = function(options = {}) {
|
var DocumentHandler = function(options) {
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
this.keyLength = options.keyLength || DocumentHandler.defaultKeyLength;
|
||||||
|
this.maxLength = options.maxLength; // none by default
|
||||||
this.store = options.store;
|
this.store = options.store;
|
||||||
this.keyGenerator = options.keyGenerator;
|
this.keyGenerator = options.keyGenerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DocumentHandler.defaultKeyLength = 10;
|
||||||
|
|
||||||
// Handle retrieving a document.
|
// Handle retrieving a document
|
||||||
DocumentHandler.prototype.handleGet = function(key, response) {
|
DocumentHandler.prototype.handleGet = function(key, response) {
|
||||||
this.store.get(key, function(ret) {
|
this.store.get(key, function(ret) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
Winston.verbose("Retrieved document.", { key: key });
|
Winston.verbose('retrieved document', { key: key });
|
||||||
response.writeHead(200, { "content-type": "application/json" });
|
response.writeHead(200, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ data: ret, key: key }));
|
response.end(JSON.stringify({ data: ret, key: key }));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Winston.warn("Document not found.", { key: key });
|
Winston.warn('document not found', { key: key });
|
||||||
response.writeHead(404, { "content-type": "application/json" });
|
response.writeHead(404, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ message: "Document not found." }));
|
response.end(JSON.stringify({ message: 'Document not found.' }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle retrieving the raw version of a document.
|
// Handle retrieving the raw version of a document
|
||||||
DocumentHandler.prototype.handleRawGet = function(key, response) {
|
DocumentHandler.prototype.handleRawGet = function(key, response) {
|
||||||
this.store.get(key, function(ret) {
|
this.store.get(key, function(ret) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
Winston.verbose("Retrieved raw document.", { key: key });
|
Winston.verbose('retrieved raw document', { key: key });
|
||||||
response.writeHead(200, { "content-type": "text/plain; charset=UTF-8" });
|
response.writeHead(200, { 'content-type': 'text/plain; charset=UTF-8' });
|
||||||
response.end(ret);
|
response.end(ret);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Winston.warn("Raw document not found.", { key: key });
|
Winston.warn('raw document not found', { key: key });
|
||||||
response.writeHead(404, { "content-type": "application/json" });
|
response.writeHead(404, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ message: "Document not found." }));
|
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;
|
||||||
var buffer = "";
|
var buffer = '';
|
||||||
var cancelled = false;
|
var cancelled = false;
|
||||||
|
|
||||||
// If success...
|
// What to do when done
|
||||||
var onSuccess = function () {
|
var onSuccess = function () {
|
||||||
|
// Check length
|
||||||
|
if (_this.maxLength && buffer.length > _this.maxLength) {
|
||||||
|
cancelled = true;
|
||||||
|
Winston.warn('document >maxLength', { maxLength: _this.maxLength });
|
||||||
|
response.writeHead(400, { 'content-type': 'application/json' });
|
||||||
|
response.end(
|
||||||
|
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 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) {
|
||||||
if (res) {
|
if (res) {
|
||||||
Winston.verbose("Added document.", { key: key });
|
Winston.verbose('added document', { key: key });
|
||||||
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 }));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Winston.verbose("Error adding document");
|
Winston.verbose('error adding document');
|
||||||
response.writeHead(500, { "content-type": "application/json" });
|
response.writeHead(500, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ message: "Error adding document." }));
|
response.end(JSON.stringify({ message: 'Error adding document.' }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse form to grab the data.
|
// If we should, parse a form to grab the data
|
||||||
var ct = request.headers["content-type"];
|
var ct = request.headers['content-type'];
|
||||||
if (ct && ct.split(";")[0] === "multipart/form-data") {
|
if (ct && ct.split(';')[0] === 'multipart/form-data') {
|
||||||
var busboy = new Busboy({ headers: request.headers });
|
var busboy = new Busboy({ headers: request.headers });
|
||||||
busboy.on("field", function (fieldname, val) {
|
busboy.on('field', function (fieldname, val) {
|
||||||
if (fieldname === "data") {
|
if (fieldname === 'data') {
|
||||||
buffer = val;
|
buffer = val;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
busboy.on("finish", function () {
|
busboy.on('finish', function () {
|
||||||
onSuccess();
|
onSuccess();
|
||||||
});
|
});
|
||||||
request.pipe(busboy);
|
request.pipe(busboy);
|
||||||
// Otherwise, use our own and just grab flat data from POST body.
|
// Otherwise, use our own and just grab flat data from POST body
|
||||||
} else {
|
} else {
|
||||||
request.on("data", function (data) {
|
request.on('data', function (data) {
|
||||||
buffer += data.toString();
|
buffer += data.toString();
|
||||||
});
|
});
|
||||||
request.on("end", function () {
|
request.on('end', function () {
|
||||||
if (cancelled) { return; }
|
if (cancelled) { return; }
|
||||||
onSuccess();
|
onSuccess();
|
||||||
});
|
});
|
||||||
request.on("error", function (error) {
|
request.on('error', function (error) {
|
||||||
Winston.error("Connection error: " + error.message);
|
Winston.error('connection error: ' + error.message);
|
||||||
response.writeHead(500, { "content-type": "application/json" });
|
response.writeHead(500, { 'content-type': 'application/json' });
|
||||||
response.end(JSON.stringify({ message: "Connection error." }));
|
response.end(JSON.stringify({ message: 'Connection error.' }));
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep choosing keys until we find one that isn't taken.
|
// Keep choosing keys until one isn't taken
|
||||||
DocumentHandler.prototype.chooseKey = function(callback) {
|
DocumentHandler.prototype.chooseKey = function(callback) {
|
||||||
var key = this.acceptableKey();
|
var key = this.acceptableKey();
|
||||||
var _this = this;
|
var _this = this;
|
||||||
@ -106,11 +123,11 @@ DocumentHandler.prototype.chooseKey = function(callback) {
|
|||||||
} else {
|
} else {
|
||||||
callback(key);
|
callback(key);
|
||||||
}
|
}
|
||||||
}, true); // Don't bump expirations when key searching.
|
}, true); // Don't bump expirations when key searching
|
||||||
};
|
};
|
||||||
|
|
||||||
DocumentHandler.prototype.acceptableKey = function() {
|
DocumentHandler.prototype.acceptableKey = function() {
|
||||||
return this.keyGenerator.createKey();
|
return this.keyGenerator.createKey(this.keyLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = DocumentHandler;
|
module.exports = DocumentHandler;
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
var fs = require("fs");
|
var fs = require('fs');
|
||||||
var crypto = require("crypto");
|
var crypto = require('crypto');
|
||||||
|
|
||||||
var FileDocumentStore = function() {
|
var FileDocumentStore = function() {
|
||||||
this.basePath = "/data";
|
this.basePath = '/data';
|
||||||
this.expire = null;
|
this.expire = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
FileDocumentStore.md5 = function(str) {
|
FileDocumentStore.md5 = function(str) {
|
||||||
var md5sum = crypto.createHash("md5");
|
var md5sum = crypto.createHash('md5');
|
||||||
md5sum.update(str);
|
md5sum.update(str);
|
||||||
return md5sum.digest("hex");
|
return md5sum.digest('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
FileDocumentStore.prototype.set = function(key, data, callback) {
|
FileDocumentStore.prototype.set = function(key, data, callback) {
|
||||||
try {
|
try {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
fs.mkdir(this.basePath, "700", function() {
|
fs.mkdir(this.basePath, '700', function() {
|
||||||
var fn = _this.basePath + "/" + FileDocumentStore.md5(key);
|
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
||||||
fs.writeFile(fn, data, "utf8", function(err) {
|
fs.writeFile(fn, data, 'utf8', function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(false);
|
callback(false);
|
||||||
}
|
}
|
||||||
@ -33,8 +33,8 @@ FileDocumentStore.prototype.set = function(key, data, callback) {
|
|||||||
|
|
||||||
FileDocumentStore.prototype.get = function(key, callback) {
|
FileDocumentStore.prototype.get = function(key, callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var fn = _this.basePath + "/" + FileDocumentStore.md5(key);
|
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
||||||
fs.readFile(fn, "utf8", function(err, data) {
|
fs.readFile(fn, 'utf8', function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(false);
|
callback(false);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
module.exports = class KeyGenerator {
|
module.exports = class KeyGenerator {
|
||||||
|
|
||||||
// Initialise a new generator with the given key space.
|
// Initialize a new generator with the given keySpace
|
||||||
constructor(options = {}) {
|
constructor() {
|
||||||
this.keyLength = options.keyLength;
|
this.keyspace = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
this.keySpace = options.keySpace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a key of the given length.
|
// Generate a key of the given length
|
||||||
createKey() {
|
createKey(keyLength) {
|
||||||
var text = "";
|
var text = '';
|
||||||
for (var i = 0; i < this.keyLength; i++) {
|
|
||||||
const index = Math.floor(Math.random() * this.keySpace.length);
|
for (var i = 0; i < keyLength; i++) {
|
||||||
text += this.keySpace.charAt(index);
|
const index = Math.floor(Math.random() * this.keyspace.length);
|
||||||
|
text += this.keyspace.charAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
"connect": "3.4.1",
|
"connect": "3.4.1",
|
||||||
"st": "1.1.0",
|
"st": "1.1.0",
|
||||||
"winston": "0.6.2",
|
"winston": "0.6.2",
|
||||||
|
"redis-url": "0.1.0",
|
||||||
|
"redis": "0.8.1",
|
||||||
|
"uglify-js": "3.1.6",
|
||||||
"busboy": "0.2.4",
|
"busboy": "0.2.4",
|
||||||
"pg": "4.1.1"
|
"pg": "4.1.1"
|
||||||
},
|
},
|
||||||
|
70
server.js
70
server.js
@ -1,25 +1,25 @@
|
|||||||
var Connect = require("connect");
|
var Connect = require('connect');
|
||||||
var ConnectSt = require("st");
|
var ConnectSt = require('st');
|
||||||
var Fs = require("fs");
|
var Fs = require('fs');
|
||||||
var Http = require("http");
|
var Http = require('http');
|
||||||
var Route = require("connect-route");
|
var Route = require('connect-route');
|
||||||
var Winston = require("winston");
|
var Winston = require('winston');
|
||||||
|
|
||||||
var DocumentHandler = require("./lib/document_handler");
|
var DocumentHandler = require('./lib/document_handler');
|
||||||
var KeyGenerator = require("./lib/key_generator");
|
var KeyGenerator = require('./lib/key_generator');
|
||||||
var Store = require("./lib/document_store");
|
var Store = require('./lib/document_store');
|
||||||
|
|
||||||
// Load the configuration and set some defaults.
|
// Load the configuration and set some defaults
|
||||||
var config = JSON.parse(Fs.readFileSync("./config.js", "utf8"));
|
var config = JSON.parse(Fs.readFileSync('./config.js', 'utf8'));
|
||||||
config.host = config.host || "127.0.0.1";
|
config.host = config.host || '127.0.0.1';
|
||||||
config.port = config.port || 7777;
|
config.port = config.port || 7777;
|
||||||
|
|
||||||
// Set up the logger.
|
// Set up the logger
|
||||||
if (config.logging) {
|
if (config.logging) {
|
||||||
try {
|
try {
|
||||||
Winston.remove(Winston.transports.Console);
|
Winston.remove(Winston.transports.Console);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// This is fine.
|
// This is fine
|
||||||
}
|
}
|
||||||
var detail, type;
|
var detail, type;
|
||||||
for (var i = 0; i < config.logging.length; i++) {
|
for (var i = 0; i < config.logging.length; i++) {
|
||||||
@ -30,62 +30,60 @@ if (config.logging) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the data store.
|
// Configure the data store
|
||||||
store = new Store();
|
store = new Store();
|
||||||
|
|
||||||
// Pick up a key generator.
|
// Pick up a key generator
|
||||||
var keyGenerator = new KeyGenerator({
|
var keyGenerator = new KeyGenerator();
|
||||||
keyLength: config.keyLength,
|
|
||||||
keySpace: config.keySpace
|
|
||||||
});
|
|
||||||
|
|
||||||
// Configure the document handler.
|
// Configure the document handler
|
||||||
var documentHandler = new DocumentHandler({
|
var documentHandler = new DocumentHandler({
|
||||||
store: store,
|
store: store,
|
||||||
|
maxLength: config.maxLength,
|
||||||
keyLength: config.keyLength,
|
keyLength: config.keyLength,
|
||||||
keyGenerator: keyGenerator
|
keyGenerator: keyGenerator
|
||||||
});
|
});
|
||||||
|
|
||||||
var app = Connect();
|
var app = Connect();
|
||||||
|
|
||||||
// Configure the API routes.
|
// API
|
||||||
app.use(Route(function(router) {
|
app.use(Route(function(router) {
|
||||||
router.get("/raw/:id", function(request, response) {
|
router.get('/raw/:id', function(request, response) {
|
||||||
var key = request.params.id.split(".")[0];
|
var key = request.params.id.split('.')[0];
|
||||||
return documentHandler.handleRawGet(key, response);
|
return documentHandler.handleRawGet(key, response);
|
||||||
});
|
});
|
||||||
router.post("/documents", function(request, response) {
|
router.post('/documents', function(request, response) {
|
||||||
return documentHandler.handlePost(request, response);
|
return documentHandler.handlePost(request, response);
|
||||||
});
|
});
|
||||||
router.get("/documents/:id", function(request, response) {
|
router.get('/documents/:id', function(request, response) {
|
||||||
var key = request.params.id.split(".")[0];
|
var key = request.params.id.split('.')[0];
|
||||||
return documentHandler.handleGet(key, response);
|
return documentHandler.handleGet(key, response);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Route static files.
|
// Static files
|
||||||
app.use(ConnectSt({
|
app.use(ConnectSt({
|
||||||
path: __dirname + "/static",
|
path: __dirname + '/static',
|
||||||
content: { maxAge: config.staticMaxAge },
|
content: { maxAge: config.staticMaxAge },
|
||||||
passthrough: true,
|
passthrough: true,
|
||||||
index: false
|
index: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// All the pastebin IDs.
|
// All the pastebin IDs
|
||||||
app.use(Route(function(router) {
|
app.use(Route(function(router) {
|
||||||
router.get("/:id", function(request, response, next) {
|
router.get('/:id', function(request, response, next) {
|
||||||
request.sturl = "/";
|
request.sturl = '/';
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Route index.html.
|
// Index
|
||||||
app.use(ConnectSt({
|
app.use(ConnectSt({
|
||||||
path: __dirname + "/static",
|
path: __dirname + '/static',
|
||||||
content: { maxAge: config.staticMaxAge },
|
content: { maxAge: config.staticMaxAge },
|
||||||
index: "index.html"
|
index: 'index.html'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Http.createServer(app).listen(config.port, config.host);
|
Http.createServer(app).listen(config.port, config.host);
|
||||||
|
|
||||||
Winston.info("Listening on " + config.host + ":" + config.port);
|
Winston.info('Listening on ' + config.host + ':' + config.port);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user