Removed Redis & MongoDB dependencies in favor of Loki.js

This commit is contained in:
NGPixel 2016-08-21 23:18:31 -04:00
parent e94abf9466
commit 24f90d4a17
11 changed files with 140 additions and 295 deletions

6
.gitignore vendored
View File

@ -45,5 +45,7 @@ jspm_packages
# Config Files # Config Files
config.yml config.yml
# App Repo # Data directories
repo /repo
/data
/uploads

View File

@ -22,21 +22,13 @@ host: http://localhost
port: 80 port: 80
# ------------------------------------------------- # -------------------------------------------------
# MongoDB Connection String # Data Directories
# ------------------------------------------------- # -------------------------------------------------
# Full explanation + examples in the documentation (https://opsstatus.readme.io/)
db: mongodb://localhost/wiki datadir:
repo: ./repo
# ------------------------------------------------- db: ./data
# Redis Connection Info uploads: ./uploads
# -------------------------------------------------
# Full explanation + examples in the documentation (https://opsstatus.readme.io/)
redis:
host: localhost
port: 6379
db: 0
# ------------------------------------------------- # -------------------------------------------------
# Git Connection Info # Git Connection Info

View File

@ -2,13 +2,13 @@ var express = require('express');
var router = express.Router(); var router = express.Router();
var passport = require('passport'); var passport = require('passport');
var ExpressBrute = require('express-brute'); var ExpressBrute = require('express-brute');
var ExpressBruteRedisStore = require('express-brute-redis'); //var ExpressBruteRedisStore = require('express-brute-redis');
var moment = require('moment'); var moment = require('moment');
/** /**
* Setup Express-Brute * Setup Express-Brute
*/ */
var EBstore = new ExpressBruteRedisStore({ /*var EBstore = new ExpressBruteRedisStore({
prefix: 'bf:', prefix: 'bf:',
client: red client: red
}); });
@ -26,7 +26,7 @@ var bruteforce = new ExpressBrute(EBstore, {
}); });
res.redirect('/login'); res.redirect('/login');
} }
}); });*/
/** /**
* Login form * Login form
@ -37,7 +37,7 @@ router.get('/login', function(req, res, next) {
}); });
}); });
router.post('/login', bruteforce.prevent, function(req, res, next) { router.post('/login', /*bruteforce.prevent,*/ function(req, res, next) {
passport.authenticate('local', function(err, user, info) { passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); } if (err) { return next(err); }
@ -54,9 +54,9 @@ router.post('/login', bruteforce.prevent, function(req, res, next) {
req.logIn(user, function(err) { req.logIn(user, function(err) {
if (err) { return next(err); } if (err) { return next(err); }
req.brute.reset(function () { //req.brute.reset(function () {
return res.redirect('/'); return res.redirect('/');
}); //});
}); });
})(req, res, next); })(req, res, next);

View File

@ -9,11 +9,12 @@ module.exports = function(passport, appconfig) {
}); });
passport.deserializeUser(function(id, done) { passport.deserializeUser(function(id, done) {
db.User.findById(id).then((user) => { let user = db.User.find({ id });
if(user) {
done(null, user); done(null, user);
}).catch((err) => { } else {
done(err, null); done(err, null);
}); }
}); });
// Setup local user authentication strategy // Setup local user authentication strategy
@ -42,23 +43,23 @@ module.exports = function(passport, appconfig) {
// Check for admin access // Check for admin access
db.connectPromise.then(() => { db.onReady.then(() => {
db.User.count().then((count) => { if(db.User.count() < 1) {
if(count < 1) {
winston.info('No administrator account found. Creating a new one...'); winston.info('No administrator account found. Creating a new one...');
db.User.new({ if(db.User.insert({
email: appconfig.admin, email: appconfig.admin,
firstName: "Admin", firstName: "Admin",
lastName: "Admin", lastName: "Admin",
password: "admin123" password: "admin123"
}).then(() => { })) {
winston.info('Administrator account created successfully!'); winston.info('Administrator account created successfully!');
}).catch((ex) => { } else {
winston.error('An error occured while creating administrator account: ' + ex); winston.error('An error occured while creating administrator account: ');
});
} }
}); }
return true;
}); });

View File

@ -1,158 +1,9 @@
"use strict"; "use strict";
var modb = require('mongoose');
var bcrypt = require('bcryptjs-then'); var bcrypt = require('bcryptjs-then');
var Promise = require('bluebird'); var Promise = require('bluebird');
var _ = require('lodash'); var _ = require('lodash');
/** module.exports = {
* User Schema
*
* @type {Object}
*/
var userSchema = modb.Schema({
email: {
type: String,
required: true,
index: true,
minlength: 6
},
password: {
type: String,
required: true
},
firstName: {
type: String,
required: true,
minlength: 1
},
lastName: {
type: String,
required: true,
minlength: 1
},
timezone: {
type: String,
required: true,
default: 'UTC'
},
lang: {
type: String,
required: true,
default: 'en'
},
rights: [{
type: String,
required: true
}]
},
{
timestamps: {}
});
/**
* VIRTUAL - Full Name
*/
userSchema.virtual('fullName').get(function() {
return this.firstName + ' ' + this.lastName;
});
/**
* INSTANCE - Validate password against hash
*
* @param {string} uPassword The user password
* @return {Promise<Boolean>} Promise with valid / invalid boolean
*/
userSchema.methods.validatePassword = function(uPassword) {
let self = this;
return bcrypt.compare(uPassword, self.password);
};
/**
* MODEL - Generate hash from password
*
* @param {string} uPassword The user password
* @return {Promise<String>} Promise with generated hash
*/
userSchema.statics.generateHash = function(uPassword) {
return bcrypt.hash(uPassword, 10);
};
/**
* MODEL - Create a new user
*
* @param {Object} nUserData User data
* @return {Promise} Promise of the create operation
*/
userSchema.statics.new = function(nUserData) {
let self = this;
return self.generateHash(nUserData.password).then((passhash) => {
return this.create({
_id: db.ObjectId(),
email: nUserData.email,
firstName: nUserData.firstName,
lastName: nUserData.lastName,
password: passhash,
rights: ['admin']
});
});
}; };
/**
* MODEL - Edit a user
*
* @param {String} userId The user identifier
* @param {Object} data The user data
* @return {Promise} Promise of the update operation
*/
userSchema.statics.edit = function(userId, data) {
let self = this;
// Change basic info
let fdata = {
email: data.email,
firstName: data.firstName,
lastName: data.lastName,
timezone: data.timezone,
lang: data.lang,
rights: data.rights
};
let waitTask = null;
// Change password?
if(!_.isEmpty(data.password) && _.trim(data.password) !== '********') {
waitTask = self.generateHash(data.password).then((passhash) => {
fdata.password = passhash;
return fdata;
});
} else {
waitTask = Promise.resolve(fdata);
}
// Update user
return waitTask.then((udata) => {
return this.findByIdAndUpdate(userId, udata, { runValidators: true });
});
};
/**
* MODEL - Delete a user
*
* @param {String} userId The user ID
* @return {Promise} Promise of the delete operation
*/
userSchema.statics.erase = function(userId) {
return this.findByIdAndRemove(userId);
};
module.exports = modb.model('User', userSchema);

35
models/localdata.js Normal file
View File

@ -0,0 +1,35 @@
"use strict";
var fs = require('fs'),
_ = require('lodash');
/**
* Local Data Storage
*
* @param {Object} appconfig The application configuration
*/
module.exports = (appconfig) => {
// Create DB folder
try {
fs.mkdirSync(appconfig.datadir.db);
} catch (err) {
if(err.code !== 'EEXIST') {
winston.error(err);
process.exit(1);
}
}
// Create Uploads folder
try {
fs.mkdirSync(appconfig.datadir.uploads);
} catch (err) {
if(err.code !== 'EEXIST') {
winston.error(err);
process.exit(1);
}
}
};

60
models/loki.js Normal file
View File

@ -0,0 +1,60 @@
"use strict";
var loki = require('lokijs'),
fs = require("fs"),
path = require("path"),
Promise = require('bluebird'),
_ = require('lodash');
/**
* Loki.js module
*
* @param {Object} appconfig Application config
* @return {Object} LokiJS instance
*/
module.exports = function(appconfig) {
let dbReadyResolve;
let dbReady = new Promise((resolve, reject) => {
dbReadyResolve = resolve;
});
// Initialize Loki.js
let dbModel = {
Store: new loki(path.join(appconfig.datadir.db, 'app.db'), {
env: 'NODEJS',
autosave: true,
autosaveInterval: 5000
}),
Models: {},
onReady: dbReady
};
// Load Models
let dbModelsPath = path.join(ROOTPATH, 'models/db');
dbModel.Store.loadDatabase({}, () => {
fs
.readdirSync(dbModelsPath)
.filter(function(file) {
return (file.indexOf(".") !== 0);
})
.forEach(function(file) {
let modelName = _.upperFirst(_.split(file,'.')[0]);
dbModel.Models[modelName] = require(path.join(dbModelsPath, file));
dbModel[modelName] = dbModel.Store.getCollection(modelName);
if(!dbModel[modelName]) {
dbModel[modelName] = dbModel.Store.addCollection(modelName);
}
});
dbReadyResolve();
});
return dbModel;
};

View File

@ -1,53 +0,0 @@
"use strict";
var modb = require('mongoose'),
fs = require("fs"),
path = require("path"),
_ = require('lodash');
/**
* MongoDB module
*
* @param {Object} appconfig Application config
* @return {Object} Mongoose instance
*/
module.exports = function(appconfig) {
modb.Promise = require('bluebird');
let dbModels = {};
let dbModelsPath = path.join(ROOTPATH, 'models/db');
// Event handlers
modb.connection.on('error', (err) => {
winston.error('Failed to connect to MongoDB instance.');
});
modb.connection.once('open', function() {
winston.log('Connected to MongoDB instance.');
});
// Store connection handle
dbModels.connection = modb.connection;
dbModels.ObjectId = modb.Types.ObjectId;
// Load Models
fs
.readdirSync(dbModelsPath)
.filter(function(file) {
return (file.indexOf(".") !== 0);
})
.forEach(function(file) {
let modelName = _.upperFirst(_.split(file,'.')[0]);
dbModels[modelName] = require(path.join(dbModelsPath, file));
});
// Connect
dbModels.connectPromise = modb.connect(appconfig.db);
return dbModels;
};

View File

@ -1,41 +0,0 @@
"use strict";
var Redis = require('ioredis'),
_ = require('lodash');
/**
* Redis module
*
* @param {Object} appconfig Application config
* @return {Redis} Redis instance
*/
module.exports = (appconfig) => {
let rd = null;
if(_.isArray(appconfig.redis)) {
rd = new Redis.Cluster(appconfig.redis, {
scaleReads: 'master',
redisOptions: {
lazyConnect: false
}
});
} else {
rd = new Redis(_.defaultsDeep(appconfig.redis), {
lazyConnect: false
});
}
// Handle connection errors
rd.on('error', (err) => {
winston.error('Failed to connect to Redis instance(s). [err-1]');
});
rd.on('node error', (err) => {
winston.error('Failed to connect to Redis instance(s). [err-2]');
});
return rd;
};

View File

@ -38,20 +38,20 @@
"cheerio": "^0.20.0", "cheerio": "^0.20.0",
"compression": "^1.6.2", "compression": "^1.6.2",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"connect-loki": "^1.0.4",
"connect-redis": "^3.1.0", "connect-redis": "^3.1.0",
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"express": "^4.14.0", "express": "^4.14.0",
"express-brute": "^0.7.0-beta.0", "express-brute": "^0.7.0-beta.0",
"express-brute-redis": "0.0.1",
"express-session": "^1.14.0", "express-session": "^1.14.0",
"express-validator": "^2.20.8", "express-validator": "^2.20.8",
"highlight.js": "^9.6.0", "highlight.js": "^9.6.0",
"i18next": "^3.4.1", "i18next": "^3.4.1",
"i18next-express-middleware": "^1.0.1", "i18next-express-middleware": "^1.0.1",
"i18next-node-fs-backend": "^0.1.2", "i18next-node-fs-backend": "^0.1.2",
"ioredis": "^2.3.0",
"js-yaml": "^3.6.1", "js-yaml": "^3.6.1",
"lodash": "^4.15.0", "lodash": "^4.15.0",
"lokijs": "^1.4.1",
"markdown-it": "^7.0.1", "markdown-it": "^7.0.1",
"markdown-it-abbr": "^1.0.3", "markdown-it-abbr": "^1.0.3",
"markdown-it-anchor": "^2.5.0", "markdown-it-anchor": "^2.5.0",
@ -64,8 +64,6 @@
"markdown-it-toc-and-anchor": "^4.1.1", "markdown-it-toc-and-anchor": "^4.1.1",
"moment": "^2.14.1", "moment": "^2.14.1",
"moment-timezone": "^0.5.5", "moment-timezone": "^0.5.5",
"mongoose": "^4.5.9",
"mongoose-delete": "^0.3.4",
"nodegit": "^0.14.1", "nodegit": "^0.14.1",
"passport": "^0.3.2", "passport": "^0.3.2",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
@ -86,7 +84,6 @@
"chai-as-promised": "^5.3.0", "chai-as-promised": "^5.3.0",
"codacy-coverage": "^2.0.0", "codacy-coverage": "^2.0.0",
"font-awesome": "^4.6.3", "font-awesome": "^4.6.3",
"gridlex": "^2.1.1",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-babel": "^6.1.2", "gulp-babel": "^6.1.2",
"gulp-clean-css": "^2.0.12", "gulp-clean-css": "^2.0.12",
@ -99,12 +96,12 @@
"gulp-tar": "^1.9.0", "gulp-tar": "^1.9.0",
"gulp-uglify": "^2.0.0", "gulp-uglify": "^2.0.0",
"gulp-zip": "^3.2.0", "gulp-zip": "^3.2.0",
"istanbul": "^0.4.4", "istanbul": "^0.4.5",
"jquery": "^3.1.0", "jquery": "^3.1.0",
"merge-stream": "^1.0.0", "merge-stream": "^1.0.0",
"mocha": "^3.0.2", "mocha": "^3.0.2",
"mocha-lcov-reporter": "^1.2.0", "mocha-lcov-reporter": "^1.2.0",
"nodemon": "^1.10.0", "nodemon": "^1.10.2",
"snyk": "^1.18.0", "snyk": "^1.18.0",
"vue": "^1.0.26" "vue": "^1.0.26"
} }

View File

@ -14,8 +14,9 @@ global.winston = require('winston');
winston.info('[SERVER] Requarks Wiki is initializing...'); winston.info('[SERVER] Requarks Wiki is initializing...');
var appconfig = require('./models/config')('./config.yml'); var appconfig = require('./models/config')('./config.yml');
global.db = require('./models/mongodb')(appconfig); var lcdata = require('./models/localdata')(appconfig);
global.red = require('./models/redis')(appconfig);
global.db = require('./models/loki')(appconfig);
global.git = require('./models/git').init(appconfig); global.git = require('./models/git').init(appconfig);
global.mark = require('./models/markdown'); global.mark = require('./models/markdown');
@ -28,7 +29,7 @@ var express = require('express');
var path = require('path'); var path = require('path');
var favicon = require('serve-favicon'); var favicon = require('serve-favicon');
var session = require('express-session'); var session = require('express-session');
var redisStore = require('connect-redis')(session); var lokiStore = require('connect-loki')(session);
var cookieParser = require('cookie-parser'); var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser'); var bodyParser = require('body-parser');
var flash = require('connect-flash'); var flash = require('connect-flash');
@ -68,7 +69,7 @@ var strategy = require('./models/auth')(passport, appconfig);
app.use(cookieParser()); app.use(cookieParser());
app.use(session({ app.use(session({
name: 'requarkswiki.sid', name: 'requarkswiki.sid',
store: new redisStore({ client: red }), store: new lokiStore({ path: path.join(appconfig.datadir.db, 'sessions.db') }),
secret: appconfig.sessionSecret, secret: appconfig.sessionSecret,
resave: false, resave: false,
saveUninitialized: false saveUninitialized: false