Base server logic
This commit is contained in:
parent
991efbb1bc
commit
0f96377e30
13
controllers/admin.js
Normal file
13
controllers/admin.js
Normal 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
73
controllers/auth.js
Normal 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
13
controllers/pages.js
Normal 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
17
middlewares/flash.js
Normal 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
65
models/auth.js
Normal 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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
@ -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
173
server.js
@ -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
1
views/auth/login.pug
Normal file
@ -0,0 +1 @@
|
||||
DUDE
|
21
views/error.pug
Normal file
21
views/error.pug
Normal 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
28
views/layout.pug
Normal 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
|
Loading…
Reference in New Issue
Block a user