From 6271da2ff3860519f32fd536173d70f7336b43d6 Mon Sep 17 00:00:00 2001 From: Jack Hadrill Date: Mon, 8 Jun 2020 20:56:17 +0100 Subject: [PATCH] Simplify for Butlersaurus --- Dockerfile | 5 + Jenkinsfile | 0 about.md | 61 ---------- config.js | 38 +----- lib/document_handler.js | 103 +++++++--------- lib/document_store.js | 47 +++++++ lib/document_stores/amazon-s3.js | 56 --------- lib/document_stores/file.js | 63 ---------- lib/document_stores/memcached.js | 52 -------- lib/document_stores/postgres.js | 79 ------------ lib/document_stores/redis.js | 89 -------------- lib/document_stores/rethinkdb.js | 46 ------- lib/key_generator.js | 19 +++ lib/key_generators/dictionary.js | 32 ----- lib/key_generators/phonetic.js | 27 ----- lib/key_generators/random.js | 20 --- package-lock.json | 5 - package.json | 4 - server.js | 162 ++++++++----------------- test/document_handler_spec.js | 26 ---- test/key_generators/dictionary_spec.js | 33 ----- test/key_generators/phonetic_spec.js | 27 ----- test/key_generators/random_spec.js | 19 --- test/redis_document_store_spec.js | 54 --------- 24 files changed, 166 insertions(+), 901 deletions(-) create mode 100644 Dockerfile create mode 100644 Jenkinsfile delete mode 100644 about.md create mode 100644 lib/document_store.js delete mode 100644 lib/document_stores/amazon-s3.js delete mode 100644 lib/document_stores/file.js delete mode 100644 lib/document_stores/memcached.js delete mode 100644 lib/document_stores/postgres.js delete mode 100644 lib/document_stores/redis.js delete mode 100644 lib/document_stores/rethinkdb.js create mode 100644 lib/key_generator.js delete mode 100644 lib/key_generators/dictionary.js delete mode 100644 lib/key_generators/phonetic.js delete mode 100644 lib/key_generators/random.js delete mode 100644 test/document_handler_spec.js delete mode 100644 test/key_generators/dictionary_spec.js delete mode 100644 test/key_generators/phonetic_spec.js delete mode 100644 test/key_generators/random_spec.js delete mode 100644 test/redis_document_store_spec.js diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1aef86f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM node:alpine +WORKDIR /app +COPY . /app +RUN npm install +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..e69de29 diff --git a/about.md b/about.md deleted file mode 100644 index c446934..0000000 --- a/about.md +++ /dev/null @@ -1,61 +0,0 @@ -# Haste - -Sharing code is a good thing, and it should be _really_ easy to do it. -A lot of times, I want to show you something I'm seeing - and that's where we -use pastebins. - -Haste is the prettiest, easiest to use pastebin ever made. - -## Basic Usage - -Type what you want me to see, click "Save", and then copy the URL. Send that -URL to someone and they'll see what you see. - -To make a new entry, click "New" (or type 'control + n') - -## From the Console - -Most of the time I want to show you some text, it's coming from my current -console session. We should make it really easy to take code from the console -and send it to people. - -`cat something | haste` # https://hastebin.com/1238193 - -You can even take this a step further, and cut out the last step of copying the -URL with: - -* osx: `cat something | haste | pbcopy` -* linux: `cat something | haste | xsel` -* windows: check out [WinHaste](https://github.com/ajryan/WinHaste) - -After running that, the STDOUT output of `cat something` will show up at a URL -which has been conveniently copied to your clipboard. - -That's all there is to that, and you can install it with `gem install haste` -right now. - * osx: you will need to have an up to date version of Xcode - * linux: you will need to have rubygems and ruby-devel installed - -## Duration - -Pastes will stay for 30 days from their last view. They may be removed earlier -and without notice. - -## Privacy - -While the contents of hastebin.com are not directly crawled by any search robot -that obeys "robots.txt", there should be no great expectation of privacy. Post -things at your own risk. Not responsible for any loss of data or removed -pastes. - -## Open Source - -Haste can easily be installed behind your network, and it's all open source! - -* [haste-client](https://github.com/seejohnrun/haste-client) -* [haste-server](https://github.com/seejohnrun/haste-server) - -## Author - -Code by John Crepezzi -Key Design by Brian Dawson diff --git a/config.js b/config.js index 4b33e28..bf70bf6 100644 --- a/config.js +++ b/config.js @@ -1,46 +1,14 @@ { - "host": "0.0.0.0", "port": 7777, - - "keyLength": 10, - - "maxLength": 400000, - + "keyLength": 5, + "keySpace": "abcdefghijklmnopqrstuvwxyz0123456789", "staticMaxAge": 86400, - - "recompressStaticAssets": true, - "logging": [ { "level": "verbose", "type": "Console", "colorize": true } - ], - - "keyGenerator": { - "type": "phonetic" - }, - - "rateLimits": { - "categories": { - "normal": { - "totalRequests": 500, - "every": 60000 - } - } - }, - - "storage": { - "type": "memcached", - "host": "127.0.0.1", - "port": 11211, - "expire": 2592000 - }, - - "documents": { - "about": "./about.md" - } - + ] } diff --git a/lib/document_handler.js b/lib/document_handler.js index 83eb141..934e684 100644 --- a/lib/document_handler.js +++ b/lib/document_handler.js @@ -1,119 +1,102 @@ -var winston = require('winston'); var Busboy = require('busboy'); +var Winston = require('winston'); -// For handling serving stored documents +// For handling serving stored documents. -var DocumentHandler = function(options) { - if (!options) { - options = {}; - } - this.keyLength = options.keyLength || DocumentHandler.defaultKeyLength; - this.maxLength = options.maxLength; // none by default +var DocumentHandler = function(options = {}) { this.store = options.store; this.keyGenerator = options.keyGenerator; }; -DocumentHandler.defaultKeyLength = 10; -// Handle retrieving a document -DocumentHandler.prototype.handleGet = function(key, response, skipExpire) { +// Handle retrieving a document. +DocumentHandler.prototype.handleGet = function(key, response) { this.store.get(key, function(ret) { if (ret) { - winston.verbose('retrieved document', { key: key }); - response.writeHead(200, { 'content-type': 'application/json' }); + Winston.verbose("Retrieved document.", { key: key }); + response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ data: ret, key: key })); } else { - winston.warn('document not found', { key: key }); - response.writeHead(404, { 'content-type': 'application/json' }); - response.end(JSON.stringify({ message: 'Document not found.' })); + Winston.warn("Document not found.", { key: key }); + response.writeHead(404, { "content-type": "application/json" }); + response.end(JSON.stringify({ message: "Document not found." })); } - }, skipExpire); + }); }; -// Handle retrieving the raw version of a document -DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) { +// Handle retrieving the raw version of a document. +DocumentHandler.prototype.handleRawGet = function(key, response) { this.store.get(key, function(ret) { if (ret) { - winston.verbose('retrieved raw document', { key: key }); - response.writeHead(200, { 'content-type': 'text/plain; charset=UTF-8' }); + Winston.verbose("Retrieved raw document.", { key: key }); + response.writeHead(200, { "content-type": "text/plain; charset=UTF-8" }); response.end(ret); } else { - winston.warn('raw document not found', { key: key }); - response.writeHead(404, { 'content-type': 'application/json' }); - response.end(JSON.stringify({ message: 'Document not found.' })); + Winston.warn("Raw document not found.", { key: key }); + response.writeHead(404, { "content-type": "application/json" }); + response.end(JSON.stringify({ message: "Document not found." })); } - }, skipExpire); + }); }; -// Handle adding a new Document +// Handle adding a new document. DocumentHandler.prototype.handlePost = function (request, response) { var _this = this; - var buffer = ''; + var buffer = ""; var cancelled = false; - // What to do when done + // If success... 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.store.set(key, buffer, function (res) { if (res) { - winston.verbose('added document', { key: key }); - response.writeHead(200, { 'content-type': 'application/json' }); + Winston.verbose("Added document.", { key: key }); + response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ key: key })); } else { - winston.verbose('error adding document'); - response.writeHead(500, { 'content-type': 'application/json' }); - response.end(JSON.stringify({ message: 'Error adding document.' })); + Winston.verbose("Error adding document"); + response.writeHead(500, { "content-type": "application/json" }); + response.end(JSON.stringify({ message: "Error adding document." })); } }); }); }; - // If we should, parse a form to grab the data - var ct = request.headers['content-type']; - if (ct && ct.split(';')[0] === 'multipart/form-data') { + // Parse form to grab the data. + var ct = request.headers["content-type"]; + if (ct && ct.split(";")[0] === "multipart/form-data") { var busboy = new Busboy({ headers: request.headers }); - busboy.on('field', function (fieldname, val) { - if (fieldname === 'data') { + busboy.on("field", function (fieldname, val) { + if (fieldname === "data") { buffer = val; } }); - busboy.on('finish', function () { + busboy.on("finish", function () { onSuccess(); }); 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 { - request.on('data', function (data) { + request.on("data", function (data) { buffer += data.toString(); }); - request.on('end', function () { + request.on("end", function () { if (cancelled) { return; } onSuccess(); }); - request.on('error', function (error) { - winston.error('connection error: ' + error.message); - response.writeHead(500, { 'content-type': 'application/json' }); - response.end(JSON.stringify({ message: 'Connection error.' })); + request.on("error", function (error) { + Winston.error("Connection error: " + error.message); + response.writeHead(500, { "content-type": "application/json" }); + response.end(JSON.stringify({ message: "Connection error." })); 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) { var key = this.acceptableKey(); var _this = this; @@ -123,11 +106,11 @@ DocumentHandler.prototype.chooseKey = function(callback) { } else { callback(key); } - }, true); // Don't bump expirations when key searching + }, true); // Don't bump expirations when key searching. }; DocumentHandler.prototype.acceptableKey = function() { - return this.keyGenerator.createKey(this.keyLength); + return this.keyGenerator.createKey(); }; module.exports = DocumentHandler; diff --git a/lib/document_store.js b/lib/document_store.js new file mode 100644 index 0000000..1c2e546 --- /dev/null +++ b/lib/document_store.js @@ -0,0 +1,47 @@ +var fs = require("fs"); +var crypto = require("crypto"); + +var FileDocumentStore = function() { + this.basePath = "/data"; + this.expire = null; +}; + +FileDocumentStore.md5 = function(str) { + var md5sum = crypto.createHash("md5"); + md5sum.update(str); + return md5sum.digest("hex"); +}; + +FileDocumentStore.prototype.set = function(key, data, callback) { + try { + var _this = this; + fs.mkdir(this.basePath, "700", function() { + var fn = _this.basePath + "/" + FileDocumentStore.md5(key); + fs.writeFile(fn, data, "utf8", function(err) { + if (err) { + callback(false); + } + else { + callback(true); + } + }); + }); + } catch(err) { + callback(false); + } +}; + +FileDocumentStore.prototype.get = function(key, callback) { + var _this = this; + var fn = _this.basePath + "/" + FileDocumentStore.md5(key); + fs.readFile(fn, "utf8", function(err, data) { + if (err) { + callback(false); + } + else { + callback(data); + } + }); +}; + +module.exports = FileDocumentStore; diff --git a/lib/document_stores/amazon-s3.js b/lib/document_stores/amazon-s3.js deleted file mode 100644 index 11dd85d..0000000 --- a/lib/document_stores/amazon-s3.js +++ /dev/null @@ -1,56 +0,0 @@ -/*global require,module,process*/ - -var AWS = require('aws-sdk'); -var winston = require('winston'); - -var AmazonS3DocumentStore = function(options) { - this.expire = options.expire; - this.bucket = options.bucket; - this.client = new AWS.S3({region: options.region}); -}; - -AmazonS3DocumentStore.prototype.get = function(key, callback, skipExpire) { - var _this = this; - - var req = { - Bucket: _this.bucket, - Key: key - }; - - _this.client.getObject(req, function(err, data) { - if(err) { - callback(false); - } - else { - callback(data.Body.toString('utf-8')); - if (_this.expire && !skipExpire) { - winston.warn('amazon s3 store cannot set expirations on keys'); - } - } - }); -} - -AmazonS3DocumentStore.prototype.set = function(key, data, callback, skipExpire) { - var _this = this; - - var req = { - Bucket: _this.bucket, - Key: key, - Body: data, - ContentType: 'text/plain' - }; - - _this.client.putObject(req, function(err, data) { - if (err) { - callback(false); - } - else { - callback(true); - if (_this.expire && !skipExpire) { - winston.warn('amazon s3 store cannot set expirations on keys'); - } - } - }); -} - -module.exports = AmazonS3DocumentStore; diff --git a/lib/document_stores/file.js b/lib/document_stores/file.js deleted file mode 100644 index 7fd5995..0000000 --- a/lib/document_stores/file.js +++ /dev/null @@ -1,63 +0,0 @@ -var fs = require('fs'); -var crypto = require('crypto'); - -var winston = require('winston'); - -// For storing in files -// options[type] = file -// options[path] - Where to store - -var FileDocumentStore = function(options) { - this.basePath = options.path || './data'; - this.expire = options.expire; -}; - -// Generate md5 of a string -FileDocumentStore.md5 = function(str) { - var md5sum = crypto.createHash('md5'); - md5sum.update(str); - return md5sum.digest('hex'); -}; - -// Save data in a file, key as md5 - since we don't know what we could -// be passed here -FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) { - try { - var _this = this; - fs.mkdir(this.basePath, '700', function() { - var fn = _this.basePath + '/' + FileDocumentStore.md5(key); - fs.writeFile(fn, data, 'utf8', function(err) { - if (err) { - callback(false); - } - else { - callback(true); - if (_this.expire && !skipExpire) { - winston.warn('file store cannot set expirations on keys'); - } - } - }); - }); - } catch(err) { - callback(false); - } -}; - -// Get data from a file from key -FileDocumentStore.prototype.get = function(key, callback, skipExpire) { - var _this = this; - var fn = this.basePath + '/' + FileDocumentStore.md5(key); - fs.readFile(fn, 'utf8', function(err, data) { - if (err) { - callback(false); - } - else { - callback(data); - if (_this.expire && !skipExpire) { - winston.warn('file store cannot set expirations on keys'); - } - } - }); -}; - -module.exports = FileDocumentStore; diff --git a/lib/document_stores/memcached.js b/lib/document_stores/memcached.js deleted file mode 100644 index be10db6..0000000 --- a/lib/document_stores/memcached.js +++ /dev/null @@ -1,52 +0,0 @@ -const memcached = require('memcached'); -const winston = require('winston'); - -class MemcachedDocumentStore { - - // Create a new store with options - constructor(options) { - this.expire = options.expire; - - const host = options.host || '127.0.0.1'; - const port = options.port || 11211; - const url = `${host}:${port}`; - this.connect(url); - } - - // Create a connection - connect(url) { - this.client = new memcached(url); - - winston.info(`connecting to memcached on ${url}`); - - this.client.on('failure', function(error) { - winston.info('error connecting to memcached', {error}); - }); - } - - // Save file in a key - set(key, data, callback, skipExpire) { - this.client.set(key, data, skipExpire ? 0 : this.expire, (error) => { - callback(!error); - }); - } - - // Get a file from a key - get(key, callback, skipExpire) { - this.client.get(key, (error, data) => { - callback(error ? false : data); - - // Update the key so that the expiration is pushed forward - if (!skipExpire) { - this.set(key, data, (updateSucceeded) => { - if (!updateSucceeded) { - winston.error('failed to update expiration on GET', {key}); - } - }, skipExpire); - } - }); - } - -} - -module.exports = MemcachedDocumentStore; diff --git a/lib/document_stores/postgres.js b/lib/document_stores/postgres.js deleted file mode 100644 index dbb471c..0000000 --- a/lib/document_stores/postgres.js +++ /dev/null @@ -1,79 +0,0 @@ -/*global require,module,process*/ - -var postgres = require('pg'); -var winston = require('winston'); - -// create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key)); - -// A postgres document store -var PostgresDocumentStore = function (options) { - this.expireJS = options.expire; - this.connectionUrl = process.env.DATABASE_URL || options.connectionUrl; -}; - -PostgresDocumentStore.prototype = { - - // Set a given key - set: function (key, data, callback, skipExpire) { - var now = Math.floor(new Date().getTime() / 1000); - var that = this; - this.safeConnect(function (err, client, done) { - if (err) { return callback(false); } - client.query('INSERT INTO entries (key, value, expiration) VALUES ($1, $2, $3)', [ - key, - data, - that.expireJS && !skipExpire ? that.expireJS + now : null - ], function (err) { - if (err) { - winston.error('error persisting value to postgres', { error: err }); - return callback(false); - } - callback(true); - done(); - }); - }); - }, - - // Get a given key's data - get: function (key, callback, skipExpire) { - var now = Math.floor(new Date().getTime() / 1000); - var that = this; - this.safeConnect(function (err, client, done) { - if (err) { return callback(false); } - client.query('SELECT id,value,expiration 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); - } - callback(result.rows.length ? result.rows[0].value : false); - if (result.rows.length && that.expireJS && !skipExpire) { - client.query('UPDATE entries SET expiration = $1 WHERE ID = $2', [ - that.expireJS + now, - result.rows[0].id - ], function (err) { - if (!err) { - done(); - } - }); - } else { - done(); - } - }); - }); - }, - - // A connection wrapper - safeConnect: function (callback) { - postgres.connect(this.connectionUrl, function (err, client, done) { - if (err) { - winston.error('error connecting to postgres', { error: err }); - callback(err); - } else { - callback(undefined, client, done); - } - }); - } - -}; - -module.exports = PostgresDocumentStore; diff --git a/lib/document_stores/redis.js b/lib/document_stores/redis.js deleted file mode 100644 index eed07e7..0000000 --- a/lib/document_stores/redis.js +++ /dev/null @@ -1,89 +0,0 @@ -var redis = require('redis'); -var winston = require('winston'); - -// For storing in redis -// options[type] = redis -// options[host] - The host to connect to (default localhost) -// options[port] - The port to connect to (default 5379) -// options[db] - The db to use (default 0) -// options[expire] - The time to live for each key set (default never) - -var RedisDocumentStore = function(options, client) { - this.expire = options.expire; - if (client) { - winston.info('using predefined redis client'); - RedisDocumentStore.client = client; - } else if (!RedisDocumentStore.client) { - winston.info('configuring redis'); - RedisDocumentStore.connect(options); - } -}; - -// Create a connection according to config -RedisDocumentStore.connect = function(options) { - var host = options.host || '127.0.0.1'; - var port = options.port || 6379; - var index = options.db || 0; - RedisDocumentStore.client = redis.createClient(port, host); - // authenticate if password is provided - if (options.password) { - RedisDocumentStore.client.auth(options.password); - } - - RedisDocumentStore.client.on('error', function(err) { - winston.error('redis disconnected', err); - }); - - RedisDocumentStore.client.select(index, function(err) { - if (err) { - winston.error( - 'error connecting to redis index ' + index, - { error: err } - ); - process.exit(1); - } - else { - winston.info('connected to redis on ' + host + ':' + port + '/' + index); - } - }); -}; - -// Save file in a key -RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) { - var _this = this; - RedisDocumentStore.client.set(key, data, function(err) { - if (err) { - callback(false); - } - else { - if (!skipExpire) { - _this.setExpiration(key); - } - callback(true); - } - }); -}; - -// Expire a key in expire time if set -RedisDocumentStore.prototype.setExpiration = function(key) { - if (this.expire) { - RedisDocumentStore.client.expire(key, this.expire, function(err) { - if (err) { - winston.error('failed to set expiry on key: ' + key); - } - }); - } -}; - -// Get a file from a key -RedisDocumentStore.prototype.get = function(key, callback, skipExpire) { - var _this = this; - RedisDocumentStore.client.get(key, function(err, reply) { - if (!err && !skipExpire) { - _this.setExpiration(key); - } - callback(err ? false : reply); - }); -}; - -module.exports = RedisDocumentStore; diff --git a/lib/document_stores/rethinkdb.js b/lib/document_stores/rethinkdb.js deleted file mode 100644 index ca825af..0000000 --- a/lib/document_stores/rethinkdb.js +++ /dev/null @@ -1,46 +0,0 @@ -const crypto = require('crypto'); -const rethink = require('rethinkdbdash'); -const winston = require('winston'); - -const md5 = (str) => { - const md5sum = crypto.createHash('md5'); - md5sum.update(str); - return md5sum.digest('hex'); -}; - -class RethinkDBStore { - constructor(options) { - this.client = rethink({ - silent: true, - host: options.host || '127.0.0.1', - port: options.port || 28015, - db: options.db || 'haste', - user: options.user || 'admin', - password: options.password || '' - }); - } - - set(key, data, callback) { - this.client.table('uploads').insert({ id: md5(key), data: data }).run((error) => { - if (error) { - callback(false); - winston.error('failed to insert to table', error); - return; - } - callback(true); - }); - } - - get(key, callback) { - this.client.table('uploads').get(md5(key)).run((error, result) => { - if (error || !result) { - callback(false); - if (error) winston.error('failed to insert to table', error); - return; - } - callback(result.data); - }); - } -} - -module.exports = RethinkDBStore; diff --git a/lib/key_generator.js b/lib/key_generator.js new file mode 100644 index 0000000..35640a8 --- /dev/null +++ b/lib/key_generator.js @@ -0,0 +1,19 @@ +module.exports = class KeyGenerator { + + // Initialise a new generator with the given key space. + constructor(options = {}) { + this.keyLength = options.keyLength; + this.keySpace = options.keySpace; + } + + // Generate a key of the given length. + createKey() { + var text = ""; + for (var i = 0; i < this.keyLength; i++) { + const index = Math.floor(Math.random() * this.keySpace.length); + text += this.keySpace.charAt(index); + } + return text; + } + +}; diff --git a/lib/key_generators/dictionary.js b/lib/key_generators/dictionary.js deleted file mode 100644 index 0bcbc2e..0000000 --- a/lib/key_generators/dictionary.js +++ /dev/null @@ -1,32 +0,0 @@ -const fs = require('fs'); - -module.exports = class DictionaryGenerator { - - constructor(options, readyCallback) { - // Check options format - if (!options) throw Error('No options passed to generator'); - if (!options.path) throw Error('No dictionary path specified in options'); - - // Load dictionary - fs.readFile(options.path, 'utf8', (err, data) => { - if (err) throw err; - - this.dictionary = data.split(/[\n\r]+/); - - if (readyCallback) readyCallback(); - }); - } - - // Generates a dictionary-based key, of keyLength words - createKey(keyLength) { - let text = ''; - - for (let i = 0; i < keyLength; i++) { - const index = Math.floor(Math.random() * this.dictionary.length); - text += this.dictionary[index]; - } - - return text; - } - -}; diff --git a/lib/key_generators/phonetic.js b/lib/key_generators/phonetic.js deleted file mode 100644 index f281f6b..0000000 --- a/lib/key_generators/phonetic.js +++ /dev/null @@ -1,27 +0,0 @@ -// Draws inspiration from pwgen and http://tools.arantius.com/password - -const randOf = (collection) => { - return () => { - return collection[Math.floor(Math.random() * collection.length)]; - }; -}; - -// Helper methods to get an random vowel or consonant -const randVowel = randOf('aeiou'); -const randConsonant = randOf('bcdfghjklmnpqrstvwxyz'); - -module.exports = class PhoneticKeyGenerator { - - // Generate a phonetic key of alternating consonant & vowel - createKey(keyLength) { - let text = ''; - const start = Math.round(Math.random()); - - for (let i = 0; i < keyLength; i++) { - text += (i % 2 == start) ? randConsonant() : randVowel(); - } - - return text; - } - -}; diff --git a/lib/key_generators/random.js b/lib/key_generators/random.js deleted file mode 100644 index 767e26b..0000000 --- a/lib/key_generators/random.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = class RandomKeyGenerator { - - // Initialize a new generator with the given keySpace - constructor(options = {}) { - this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - } - - // Generate a key of the given length - createKey(keyLength) { - var text = ''; - - for (var i = 0; i < keyLength; i++) { - const index = Math.floor(Math.random() * this.keyspace.length); - text += this.keyspace.charAt(index); - } - - return text; - } - -}; diff --git a/package-lock.json b/package-lock.json index 14685ee..d4e6188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,11 +112,6 @@ "utils-merge": "1.0.0" } }, - "connect-ratelimit": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/connect-ratelimit/-/connect-ratelimit-0.0.7.tgz", - "integrity": "sha1-5uCclQZJ6ElJnKsYcKQVoH9zFWg=" - }, "connect-route": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz", diff --git a/package.json b/package.json index 9453b1e..5c56b8d 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,10 @@ }, "main": "haste", "dependencies": { - "connect-ratelimit": "0.0.7", "connect-route": "0.1.5", "connect": "3.4.1", "st": "1.1.0", "winston": "0.6.2", - "redis-url": "0.1.0", - "redis": "0.8.1", - "uglify-js": "3.1.6", "busboy": "0.2.4", "pg": "4.1.1" }, diff --git a/server.js b/server.js index 57bd3da..5d815f3 100644 --- a/server.js +++ b/server.js @@ -1,155 +1,91 @@ -var http = require('http'); -var fs = require('fs'); +var Connect = require("connect"); +var ConnectSt = require("st"); +var Fs = require("fs"); +var Http = require("http"); +var Route = require("connect-route"); +var Winston = require("winston"); -var uglify = require('uglify-js'); -var winston = require('winston'); -var connect = require('connect'); -var route = require('connect-route'); -var connect_st = require('st'); -var connect_rate_limit = require('connect-ratelimit'); +var DocumentHandler = require("./lib/document_handler"); +var KeyGenerator = require("./lib/key_generator"); +var Store = require("./lib/document_store"); -var DocumentHandler = require('./lib/document_handler'); +// Load the configuration and set some defaults. +var config = JSON.parse(Fs.readFileSync("./config.js", "utf8")); +config.host = config.host || "127.0.0.1"; +config.port = config.port || 7777; -// Load the configuration and set some defaults -var config = JSON.parse(fs.readFileSync('./config.js', 'utf8')); -config.port = process.env.PORT || config.port || 7777; -config.host = process.env.HOST || config.host || 'localhost'; - -// Set up the logger +// Set up the logger. if (config.logging) { try { - winston.remove(winston.transports.Console); + Winston.remove(Winston.transports.Console); } catch(e) { - /* was not present */ + // This is fine. } - var detail, type; for (var i = 0; i < config.logging.length; i++) { detail = config.logging[i]; type = detail.type; delete detail.type; - winston.add(winston.transports[type], detail); + Winston.add(Winston.transports[type], detail); } } -// build the store from the config on-demand - so that we don't load it -// for statics -if (!config.storage) { - config.storage = { type: 'file' }; -} -if (!config.storage.type) { - config.storage.type = 'file'; -} +// Configure the data store. +store = new Store(); -var Store, preferredStore; +// Pick up a key generator. +var keyGenerator = new KeyGenerator({ + keyLength: config.keyLength, + keySpace: config.keySpace +}); -if (process.env.REDISTOGO_URL && config.storage.type === 'redis') { - var redisClient = require('redis-url').connect(process.env.REDISTOGO_URL); - Store = require('./lib/document_stores/redis'); - preferredStore = new Store(config.storage, redisClient); -} -else { - Store = require('./lib/document_stores/' + config.storage.type); - preferredStore = new Store(config.storage); -} - -// Compress the static javascript assets -if (config.recompressStaticAssets) { - var list = fs.readdirSync('./static'); - for (var j = 0; j < list.length; j++) { - var item = list[j]; - if ((item.indexOf('.js') === item.length - 3) && (item.indexOf('.min.js') === -1)) { - var dest = item.substring(0, item.length - 3) + '.min' + item.substring(item.length - 3); - var orig_code = fs.readFileSync('./static/' + item, 'utf8'); - - fs.writeFileSync('./static/' + dest, uglify.minify(orig_code).code, 'utf8'); - winston.info('compressed ' + item + ' into ' + dest); - } - } -} - -// Send the static documents into the preferred store, skipping expirations -var path, data; -for (var name in config.documents) { - path = config.documents[name]; - data = fs.readFileSync(path, 'utf8'); - winston.info('loading static document', { name: name, path: path }); - if (data) { - preferredStore.set(name, data, function(cb) { - winston.debug('loaded static document', { success: cb }); - }, true); - } - else { - winston.warn('failed to load static document', { name: name, path: path }); - } -} - -// Pick up a key generator -var pwOptions = config.keyGenerator || {}; -pwOptions.type = pwOptions.type || 'random'; -var gen = require('./lib/key_generators/' + pwOptions.type); -var keyGenerator = new gen(pwOptions); - -// Configure the document handler +// Configure the document handler. var documentHandler = new DocumentHandler({ - store: preferredStore, - maxLength: config.maxLength, + store: store, keyLength: config.keyLength, keyGenerator: keyGenerator }); -var app = connect(); +var app = Connect(); -// Rate limit all requests -if (config.rateLimits) { - config.rateLimits.end = true; - app.use(connect_rate_limit(config.rateLimits)); -} - -// first look at API calls -app.use(route(function(router) { - // get raw documents - support getting with extension - router.get('/raw/:id', function(request, response) { - var key = request.params.id.split('.')[0]; - var skipExpire = !!config.documents[key]; - return documentHandler.handleRawGet(key, response, skipExpire); +// Configure the API routes. +app.use(Route(function(router) { + router.get("/raw/:id", function(request, response) { + var key = request.params.id.split(".")[0]; + return documentHandler.handleRawGet(key, response); }); - // add documents - router.post('/documents', function(request, response) { + router.post("/documents", function(request, response) { return documentHandler.handlePost(request, response); }); - // get documents - router.get('/documents/:id', function(request, response) { - var key = request.params.id.split('.')[0]; - var skipExpire = !!config.documents[key]; - return documentHandler.handleGet(key, response, skipExpire); + router.get("/documents/:id", function(request, response) { + var key = request.params.id.split(".")[0]; + return documentHandler.handleGet(key, response); }); })); -// Otherwise, try to match static files -app.use(connect_st({ - path: __dirname + '/static', +// Route static files. +app.use(ConnectSt({ + path: __dirname + "/static", content: { maxAge: config.staticMaxAge }, passthrough: true, index: false })); -// Then we can loop back - and everything else should be a token, -// so route it back to / -app.use(route(function(router) { - router.get('/:id', function(request, response, next) { - request.sturl = '/'; +// All the pastebin IDs. +app.use(Route(function(router) { + router.get("/:id", function(request, response, next) { + request.sturl = "/"; next(); }); })); -// And match index -app.use(connect_st({ - path: __dirname + '/static', +// Route index.html. +app.use(ConnectSt({ + path: __dirname + "/static", 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); diff --git a/test/document_handler_spec.js b/test/document_handler_spec.js deleted file mode 100644 index f84f066..0000000 --- a/test/document_handler_spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/* global describe, it */ - -var assert = require('assert'); - -var DocumentHandler = require('../lib/document_handler'); -var Generator = require('../lib/key_generators/random'); - -describe('document_handler', function() { - - describe('randomKey', function() { - - it('should choose a key of the proper length', function() { - var gen = new Generator(); - var dh = new DocumentHandler({ keyLength: 6, keyGenerator: gen }); - assert.equal(6, dh.acceptableKey().length); - }); - - it('should choose a default key length', function() { - var gen = new Generator(); - var dh = new DocumentHandler({ keyGenerator: gen }); - assert.equal(dh.keyLength, DocumentHandler.defaultKeyLength); - }); - - }); - -}); diff --git a/test/key_generators/dictionary_spec.js b/test/key_generators/dictionary_spec.js deleted file mode 100644 index 72718c7..0000000 --- a/test/key_generators/dictionary_spec.js +++ /dev/null @@ -1,33 +0,0 @@ -/* global describe, it */ - -const assert = require('assert'); - -const fs = require('fs'); - -const Generator = require('../../lib/key_generators/dictionary'); - -describe('RandomKeyGenerator', function() { - describe('randomKey', function() { - it('should throw an error if given no options', () => { - assert.throws(() => { - new Generator(); - }, Error); - }); - - it('should throw an error if given no path', () => { - assert.throws(() => { - new Generator({}); - }, Error); - }); - - it('should return a key of the proper number of words from the given dictionary', () => { - const path = '/tmp/haste-server-test-dictionary'; - const words = ['cat']; - fs.writeFileSync(path, words.join('\n')); - - const gen = new Generator({path}, () => { - assert.equal('catcatcat', gen.createKey(3)); - }); - }); - }); -}); diff --git a/test/key_generators/phonetic_spec.js b/test/key_generators/phonetic_spec.js deleted file mode 100644 index 14ad9e8..0000000 --- a/test/key_generators/phonetic_spec.js +++ /dev/null @@ -1,27 +0,0 @@ -/* global describe, it */ - -const assert = require('assert'); - -const Generator = require('../../lib/key_generators/phonetic'); - -const vowels = 'aeiou'; -const consonants = 'bcdfghjklmnpqrstvwxyz'; - -describe('RandomKeyGenerator', () => { - describe('randomKey', () => { - it('should return a key of the proper length', () => { - const gen = new Generator(); - assert.equal(6, gen.createKey(6).length); - }); - - it('should alternate consonants and vowels', () => { - const gen = new Generator(); - - const key = gen.createKey(3); - - assert.ok(consonants.includes(key[0])); - assert.ok(consonants.includes(key[2])); - assert.ok(vowels.includes(key[1])); - }); - }); -}); diff --git a/test/key_generators/random_spec.js b/test/key_generators/random_spec.js deleted file mode 100644 index 537a809..0000000 --- a/test/key_generators/random_spec.js +++ /dev/null @@ -1,19 +0,0 @@ -/* global describe, it */ - -const assert = require('assert'); - -const Generator = require('../../lib/key_generators/random'); - -describe('RandomKeyGenerator', () => { - describe('randomKey', () => { - it('should return a key of the proper length', () => { - const gen = new Generator(); - assert.equal(6, gen.createKey(6).length); - }); - - it('should use a key from the given keyset if given', () => { - const gen = new Generator({keyspace: 'A'}); - assert.equal('AAAAAA', gen.createKey(6)); - }); - }); -}); diff --git a/test/redis_document_store_spec.js b/test/redis_document_store_spec.js deleted file mode 100644 index 873c296..0000000 --- a/test/redis_document_store_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/* global it, describe, afterEach */ - -var assert = require('assert'); - -var winston = require('winston'); -winston.remove(winston.transports.Console); - -var RedisDocumentStore = require('../lib/document_stores/redis'); - -describe('redis_document_store', function() { - - /* reconnect to redis on each test */ - afterEach(function() { - if (RedisDocumentStore.client) { - RedisDocumentStore.client.quit(); - RedisDocumentStore.client = false; - } - }); - - describe('set', function() { - - it('should be able to set a key and have an expiration set', function(done) { - var store = new RedisDocumentStore({ expire: 10 }); - store.set('hello1', 'world', function() { - RedisDocumentStore.client.ttl('hello1', function(err, res) { - assert.ok(res > 1); - done(); - }); - }); - }); - - it('should not set an expiration when told not to', function(done) { - var store = new RedisDocumentStore({ expire: 10 }); - store.set('hello2', 'world', function() { - RedisDocumentStore.client.ttl('hello2', function(err, res) { - assert.equal(-1, res); - done(); - }); - }, true); - }); - - it('should not set an expiration when expiration is off', function(done) { - var store = new RedisDocumentStore({ expire: false }); - store.set('hello3', 'world', function() { - RedisDocumentStore.client.ttl('hello3', function(err, res) { - assert.equal(-1, res); - done(); - }); - }); - }); - - }); - -});