Compare commits

..

1 Commits

Author SHA1 Message Date
6271da2ff3 Simplify for Butlersaurus
All checks were successful
TerribleCodeClub/ButlerBin/pipeline/head This commit looks good
2020-06-08 23:18:36 +01:00
6 changed files with 96 additions and 116 deletions

View File

@ -1,10 +1,9 @@
{ {
"host": "0.0.0.0", "host": "0.0.0.0",
"port": 7777, "port": 7777,
"keyLength": 10, "keyLength": 5,
"maxLength": 400000, "keySpace": "abcdefghijklmnopqrstuvwxyz0123456789",
"staticMaxAge": 86400, "staticMaxAge": 86400,
"recompressStaticAssets": true,
"logging": [ "logging": [
{ {
"level": "verbose", "level": "verbose",

View File

@ -1,119 +1,102 @@
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;
// What to do when done // If success...
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." }));
} }
}); });
}); });
}; };
// If we should, parse a form to grab the data // Parse 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 one isn't taken // Keep choosing keys until we find one that 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;
@ -123,11 +106,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(this.keyLength); return this.keyGenerator.createKey();
}; };
module.exports = DocumentHandler; module.exports = DocumentHandler;

View File

@ -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);
} }

View File

@ -1,19 +1,18 @@
module.exports = class KeyGenerator { module.exports = class KeyGenerator {
// Initialize a new generator with the given keySpace // Initialise a new generator with the given key space.
constructor() { constructor(options = {}) {
this.keyspace = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; this.keyLength = options.keyLength;
this.keySpace = options.keySpace;
} }
// Generate a key of the given length // Generate a key of the given length.
createKey(keyLength) { createKey() {
var text = ''; var text = "";
for (var i = 0; i < this.keyLength; i++) {
for (var i = 0; i < keyLength; i++) { const index = Math.floor(Math.random() * this.keySpace.length);
const index = Math.floor(Math.random() * this.keyspace.length); text += this.keySpace.charAt(index);
text += this.keyspace.charAt(index);
} }
return text; return text;
} }

View File

@ -18,9 +18,6 @@
"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"
}, },

View File

@ -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,60 +30,62 @@ 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();
// API // Configure the API routes.
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);
}); });
})); }));
// Static files // Route 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();
}); });
})); }));
// Index // Route index.html.
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);