diff --git a/README.md b/README.md index 87eb741..e228365 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,43 @@ or post. All of which are optional except `type` with very logical default values. +### MongoDB + +To use mongodb storage you must install the 'mongodb' pachage in npm + +`npm install mongodb` + +Once you've done that, your config section should look like: + +``` json +{ + "type": "mongodb", + "expire": false, + "connectionUrl": "mongodb://localhost:27017/haste", + "clientOptions": { + "useUnifiedTopology": true, + "useNewUrlParser": true, + "keepAlive": true, + "keepAliveInitialDelay": 60000, + "poolSize": 30, + "socketTimeoutMS": 360000, + "connectTimeoutMS": 360000, + "auth": { + "user": "username", + "password": "password" + }, + "authSource": "admin" + } +} +``` + +You can adjust properties in `clientOptions`, but the ones in config are considered as the most optimal defaults. +You can omit `auth` object if database has no authentication. +No further database configuration is required. + +You can set `expire` option to the number of seconds in which documents will expire. +This is set to `false` by default, but will constantly kick back expirations on each view or post. + ### Memcached To use memcache storage you must install the `memcached` package via npm diff --git a/lib/document_stores/mongodb.js b/lib/document_stores/mongodb.js new file mode 100644 index 0000000..71adca2 --- /dev/null +++ b/lib/document_stores/mongodb.js @@ -0,0 +1,94 @@ +const winston = require('winston'); +const mongodb = require('mongodb'); + +const MongoDocumentStore = function (config){ + this.expire = config.expire; + this.MongoClient = new mongodb.MongoClient(config.connectionUrl, config.clientOptions); +}; + +MongoDocumentStore.prototype.set = async function (key, data, callback, skipExpire){ + winston.silly(`mongo set ${key}`); + const now = Math.floor(Date.now() / 1000); + const that = this; + + this.safeConnect(async ( {error} = {} ) => { + if (error) return callback(false); + + await this.MongoClient.db().collection('entries').updateOne( + { + 'entry_id': key, + $or: [ + { expiration: -1 }, + { expiration: { $gt: now } } + ] + }, + { + $set: { + 'entry_id': key, + value: data, + expiration: that.expire && !skipExpire ? that.expire + now : -1 + } + }, + { + upsert: true + } + ).then((err, result) => { + return callback(true); + }) + .catch((err, result) => { + winston.error('error updating mongodb document', { error: err }); + return callback(false); + }); + }); +}; + +MongoDocumentStore.prototype.get = async function (key, callback, skipExpire){ + winston.silly(`mongo get ${key}`); + const now = Math.floor(Date.now() / 1000); + const that = this; + + this.safeConnect(async ( {error} = {} ) => { + if (error) return callback(false); + + let document = await this.MongoClient.db().collection('entries').findOne({ + 'entry_id': key, + or: [ + { expiration: -1 }, + { expiration: { $gt: now } } + ] + }) + .catch(err => { + winston.error('error finding mongodb document', { error: err }); + return callback(false); + }); + + callback(document ? document.value : false); + + if (document && document.expiration != -1 && that.expire && !skipExpire){ + await this.MongoClient.db().collection('entries').updateOne( + { 'entry_id': key }, + { $set: { expiration: that.expire + now } } + ).catch(err => { + return winston.warn('error extending expiry of mongodb document', { error: err }); + }); + winston.silly('extended expiry of mongodb document', { key: key, timestamp: that.expire + now }) + } + }); +}; + +MongoDocumentStore.prototype.safeConnect = function(cb){ + //don't try connecting again if already connected + //https://jira.mongodb.org/browse/NODE-1868 + if (this.MongoClient.isConnected()) return cb({error: null}); + this.MongoClient.connect() + .then(client => { + winston.debug('connected to mongodb', { success: true }); + cb({error: null}); + }) + .catch(err => { + winston.error('error connecting to mongodb', { error: err }); + cb({error: err}); + }); +}; + +module.exports = MongoDocumentStore; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 400dec0..082a987 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "haste", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5250f22..2cc9de8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haste", - "version": "0.2.1", + "version": "0.2.2", "description": "Private Pastebin Server - continued by zneix", "keywords": [ "paste",