Base server logic

This commit is contained in:
NGPixel 2016-08-16 23:56:08 -04:00
parent 991efbb1bc
commit 0f96377e30
10 changed files with 404 additions and 3 deletions

13
controllers/admin.js Normal file
View File

@ -0,0 +1,13 @@
"use strict";
var express = require('express');
var router = express.Router();
/**
* Admin
*/
router.get('/', (req, res) => {
res.send('OK');
});
module.exports = router;

73
controllers/auth.js Normal file
View File

@ -0,0 +1,73 @@
var express = require('express');
var router = express.Router();
var passport = require('passport');
var ExpressBrute = require('express-brute');
var ExpressBruteRedisStore = require('express-brute-redis');
var moment = require('moment');
/**
* Setup Express-Brute
*/
var EBstore = new ExpressBruteRedisStore({
prefix: 'bf:',
client: red
});
var bruteforce = new ExpressBrute(EBstore, {
freeRetries: 5,
minWait: 60 * 1000,
maxWait: 5 * 60 * 1000,
refreshTimeoutOnRequest: false,
failCallback(req, res, next, nextValidRequestDate) {
req.flash('alert', {
class: 'error',
title: 'Too many attempts!',
message: "You've made too many failed attempts in a short period of time, please try again " + moment(nextValidRequestDate).fromNow() + '.',
iconClass: 'fa-times'
});
res.redirect('/login');
}
});
/**
* Login form
*/
router.get('/login', function(req, res, next) {
res.render('auth/login', {
usr: res.locals.usr
});
});
router.post('/login', bruteforce.prevent, function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) {
req.flash('alert', {
class: 'error',
title: 'Invalid login',
message: "The email or password is invalid.",
iconClass: 'fa-times'
});
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) { return next(err); }
req.brute.reset(function () {
return res.redirect('/');
});
});
})(req, res, next);
});
/**
* Logout
*/
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
module.exports = router;

13
controllers/pages.js Normal file
View File

@ -0,0 +1,13 @@
"use strict";
var express = require('express');
var router = express.Router();
/**
* Home
*/
router.get('/', (req, res) => {
res.send('OK');
});
module.exports = router;

17
middlewares/flash.js Normal file
View File

@ -0,0 +1,17 @@
"use strict";
/**
* Flash middleware
*
* @param {Express Request} req Express Request object
* @param {Express Response} res Express Response object
* @param {Function} next Next callback function
* @return {any} void
*/
module.exports = (req, res, next) => {
res.locals.appflash = req.flash('alert');
next();
};

65
models/auth.js Normal file
View File

@ -0,0 +1,65 @@
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(passport, appconfig) {
// Serialization user methods
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
db.User.findById(id).then((user) => {
done(null, user);
}).catch((err) => {
done(err, null);
});
});
// Setup local user authentication strategy
passport.use(
'local',
new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, uEmail, uPassword, done) {
db.User.findOne({ 'email' : uEmail }).then((user) => {
if (user) {
user.validatePassword(uPassword).then((isValid) => {
return (isValid) ? done(null, user) : done(null, false);
});
} else {
return done(null, false);
}
}).catch((err) => {
done(err);
});
})
);
// Check for admin access
db.connectPromise.then(() => {
db.User.count().then((count) => {
if(count < 1) {
winston.info('No administrator account found. Creating a new one...');
db.User.new({
email: appconfig.admin,
firstName: "Admin",
lastName: "Admin",
password: "admin123"
}).then(() => {
winston.info('Administrator account created successfully!');
}).catch((ex) => {
winston.error('An error occured while creating administrator account: ' + ex);
});
}
});
});
};

View File

@ -31,6 +31,7 @@
},
"dependencies": {
"auto-load": "^2.1.0",
"bcryptjs-then": "^1.0.1",
"bluebird": "^3.4.1",
"body-parser": "^1.15.2",
"compression": "^1.6.2",
@ -54,7 +55,7 @@
"moment-timezone": "^0.5.5",
"mongoose": "^4.5.9",
"mongoose-delete": "^0.3.4",
"node-bcrypt": "0.0.1",
"nodegit": "^0.14.1",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"pug": "^2.0.0-beta5",

173
server.js
View File

@ -14,5 +14,174 @@ winston.info('Requarks Wiki is initializing...');
global.ROOTPATH = __dirname;
var appconfig = require('./models/config')('./config.yml');
global.db = require('./models/db')(appconfig);
global.red = require('./models/redis')(appconfig);
global.db = require('./models/mongodb')(appconfig);
global.red = require('./models/redis')(appconfig);
var _ = require('lodash');
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var flash = require('connect-flash');
var compression = require('compression');
var passport = require('passport');
var autoload = require('auto-load');
var expressValidator = require('express-validator');
var http = require('http');
global.lang = require('i18next');
var i18next_backend = require('i18next-node-fs-backend');
var i18next_mw = require('i18next-express-middleware');
var mw = autoload(path.join(ROOTPATH, '/middlewares'));
var ctrl = autoload(path.join(ROOTPATH, '/controllers'));
// ----------------------------------------
// Define Express App
// ----------------------------------------
global.app = express();
global.ROOTPATH = __dirname;
var _isDebug = (app.get('env') === 'development');
// ----------------------------------------
// Security
// ----------------------------------------
app.use(mw.security);
// ----------------------------------------
// Passport Authentication
// ----------------------------------------
var strategy = require('./models/auth')(passport, appconfig);
app.use(cookieParser());
app.use(session({
name: 'requarkswiki.sid',
store: new redisStore({ client: red }),
secret: appconfig.sessionSecret,
resave: false,
saveUninitialized: false
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
// ----------------------------------------
// Localization Engine
// ----------------------------------------
lang
.use(i18next_backend)
.use(i18next_mw.LanguageDetector)
.init({
load: 'languageOnly',
ns: ['common'],
defaultNS: 'common',
saveMissing: false,
supportedLngs: ['en', 'fr'],
preload: ['en', 'fr'],
fallbackLng : 'en',
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json'
}
});
// ----------------------------------------
// View Engine Setup
// ----------------------------------------
app.use(compression());
app.use(i18next_mw.handle(lang));
app.set('views', path.join(ROOTPATH, 'views'));
app.set('view engine', 'pug');
//app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressValidator());
// ----------------------------------------
// Public Assets
// ----------------------------------------
app.use(express.static(path.join(ROOTPATH, 'assets')));
// ----------------------------------------
// View accessible data
// ----------------------------------------
app.locals._ = require('lodash');
app.locals.moment = require('moment');
app.locals.appconfig = appconfig;
//app.locals.appdata = require('./data.json');
app.use(mw.flash);
// ----------------------------------------
// Controllers
// ----------------------------------------
app.use('/', ctrl.auth);
app.use('/', ctrl.pages);
app.use('/admin', mw.auth, ctrl.admin);
// ----------------------------------------
// Error handling
// ----------------------------------------
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: _isDebug ? err : {}
});
});
// ----------------------------------------
// Start HTTP server
// ----------------------------------------
winston.info('Requarks Wiki has initialized successfully.');
winston.info('Starting HTTP server on port ' + appconfig.port + '...');
app.set('port', appconfig.port);
var server = http.createServer(app);
server.listen(appconfig.port);
server.on('error', (error) => {
if (error.syscall !== 'listen') {
throw error;
}
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error('Listening on port ' + appconfig.port + ' requires elevated privileges!');
process.exit(1);
break;
case 'EADDRINUSE':
console.error('Port ' + appconfig.port + ' is already in use!');
process.exit(1);
break;
default:
throw error;
}
});
server.on('listening', () => {
winston.info('HTTP server started successfully! [RUNNING]');
});

1
views/auth/login.pug Normal file
View File

@ -0,0 +1 @@
DUDE

21
views/error.pug Normal file
View File

@ -0,0 +1,21 @@
doctype html
html
head
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(charset='UTF-8')
title= appconfig.title
// Favicon
each favsize in [32, 96, 16]
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize href='/images/favicon-' + favsize + 'x' + favsize + '.png')
// CSS
link(href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700|Inconsolata', rel='stylesheet', type='text/css')
link(type='text/css', rel='stylesheet', href='/css/app.css')
body(class='server-error')
#root
img(src='/images/logo-text_218x80.png')
h1 Oops, something went wrong
h4= message
pre #{error.stack}

28
views/layout.pug Normal file
View File

@ -0,0 +1,28 @@
doctype html
html
head
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(charset='UTF-8')
meta(name='theme-color', content='#009688')
meta(name='msapplication-TileColor', content='#009688')
title= appconfig.title
// Favicon
each favsize in [32, 96, 16]
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize href='/images/favicon-' + favsize + 'x' + favsize + '.png')
// CSS
link(type='text/css', rel='stylesheet', href='/css/libs.css')
link(type='text/css', rel='stylesheet', href='/css/app.css')
block head
body
#root
include ./common/header
include ./common/alerts
main
block content
include ./common/footer
block outside