diff --git a/package.json b/package.json index 761b21a0fe..0065c662fa 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,15 @@ "connect-flash": "^0.1.1", "cron": "~1.0.4", "daemon": "~1.1.0", - "express": "3.2.0", - "express-namespace": "~0.1.1", + "express": "4.4.5", + "cookie-parser": "^1.0.1", + "body-parser": "^1.0.1", + "serve-favicon": "^2.0.1", + "express-session": "^1.0.2", + "csurf": "^1.1.0", + "compression": "^1.0.1", + "connect-multiparty": "^1.0.1", + "morgan": "^1.0.0", "gm": "1.14.2", "gravatar": "1.0.6", "less": "~1.6.3", @@ -52,7 +59,7 @@ "validator": "~3.4.0", "winston": "~0.7.2", "xregexp": "~2.0.0", - "templates.js": "0.0.6" + "templates.js": "0.0.7" }, "devDependencies": { "mocha": "~1.13.0" diff --git a/public/src/templates.js b/public/src/templates.js index 3d02dfcc87..5cf10aeb4b 100644 --- a/public/src/templates.js +++ b/public/src/templates.js @@ -36,7 +36,7 @@ Please use the npm module instead - require('templates.js') } callback(parse(loaded, obj, bind)); - }); + }); } else { callback(parse(templates.cache[template], obj, bind)); } @@ -62,6 +62,7 @@ Please use the npm module instead - require('templates.js') }; function express(filename, options, fn) { + console.log(filename, options, fn); var fs = require('fs'), tpl = filename.replace(options.settings.views + '/', ''); @@ -185,7 +186,7 @@ Please use the npm module instead - require('templates.js') while (block = template.match(regex)) { block = block[0].replace(makeBlockRegex(key), ''); - + var numblocks = array[key].length - 1, iterator = 0, result = '', @@ -193,7 +194,7 @@ Please use the npm module instead - require('templates.js') do { parsedBlock = parse(block, array[key][iterator], bind, namespace, {iterator: iterator, total: numblocks}) + ((iterator < numblocks) ? '\r\n':''); - + result += (!bind) ? parsedBlock : setBindContainer(parsedBlock, bind + namespace + iterator); result = parseFunctions(block, result, { data: array[key][iterator], @@ -213,7 +214,7 @@ Please use the npm module instead - require('templates.js') template = template.replace(regex, result); } - + return template; } @@ -247,14 +248,14 @@ Please use the npm module instead - require('templates.js') this['__' + key] = value; var els = document.querySelectorAll('[data-binding="' + (this.__iterator !== false ? (bind + this.__namespace + this.__iterator) : bind) + '"]'); - + for (var el in els) { if (els.hasOwnProperty(el)) { if (this.__parent) { var parent = this.__parent(); els[el].innerHTML = parse(parent.template, parent.data, false); } else { - els[el].innerHTML = parse(this.__template, obj, false, this.__namespace); + els[el].innerHTML = parse(this.__template, obj, false, this.__namespace); } } } @@ -294,7 +295,7 @@ Please use the npm module instead - require('templates.js') template = parse(template, obj[key], bind, namespace + key + '.'); } else { template = parseValue(template, namespace + key, obj[key]); - + if (bind && obj[key]) { setupBindings({ obj: obj, diff --git a/src/categories.js b/src/categories.js index 58c790c1a4..80ed12e621 100644 --- a/src/categories.js +++ b/src/categories.js @@ -275,7 +275,7 @@ var db = require('./database'), return callback(err); } - if (!Array.isArray(categories)) { + if (!Array.isArray(categories) || !categories.length) { return callback(null, []); } diff --git a/src/database/mongo.js b/src/database/mongo.js index 5b25ae9284..a9f765e1dd 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -6,7 +6,7 @@ var winston = require('winston'), async = require('async'), nconf = require('nconf'), - express = require('express'), + session = require('express-session'), db, mongoClient, mongoStore; @@ -41,7 +41,7 @@ module.init = function(callback) { try { mongoClient = require('mongodb').MongoClient; - mongoStore = require('connect-mongo')(express); + mongoStore = require('connect-mongo')({session: session}); } catch (err) { winston.error('Unable to initialize MongoDB! Is MongoDB installed? Error :' + err.message); process.exit(); diff --git a/src/database/redis.js b/src/database/redis.js index 2c0e9bd1cc..0becf2ba9e 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -5,7 +5,7 @@ var winston = require('winston'), nconf = require('nconf'), path = require('path'), - express = require('express'), + session = require('express-session'), utils = require('./../../public/src/utils.js'), redis, connectRedis, @@ -40,7 +40,7 @@ module.init = function(callback) { try { redis = require('redis'); - connectRedis = require('connect-redis')(express); + connectRedis = require('connect-redis')(session); reds = require('reds'); } catch (err) { winston.error('Unable to initialize Redis! Is Redis installed? Error :' + err.message); diff --git a/src/install.js b/src/install.js index b6d062cbb0..2fe604029b 100644 --- a/src/install.js +++ b/src/install.js @@ -11,7 +11,7 @@ var async = require('async'), DATABASES = { "redis": { - "dependencies": ["redis@~0.10.1", "connect-redis@~1.4"] + "dependencies": ["redis@~0.10.1", "connect-redis@~2.0.0"] }, "mongo": { "dependencies": ["mongodb", "connect-mongo"] @@ -366,26 +366,25 @@ function createCategories(next) { var Categories = require('./categories'); Categories.getAllCategories(function (err, categoryData) { - if (categoryData.length === 0) { - winston.warn('No categories found, populating instance with default categories'); - - fs.readFile(path.join(__dirname, '../', 'install/data/categories.json'), function (err, default_categories) { - default_categories = JSON.parse(default_categories); - - async.eachSeries(default_categories, function (category, next) { - Categories.create(category, next); - }, function (err) { - if (!err) { - next(); - } else { - winston.error('Could not set up categories'); - } - }); - }); - } else { + if (err) { + return next(err); + } + + if (Array.isArray(categoryData) && categoryData.length) { winston.info('Categories OK. Found ' + categoryData.length + ' categories.'); - next(); + return next(); } + + winston.warn('No categories found, populating instance with default categories'); + + fs.readFile(path.join(__dirname, '../', 'install/data/categories.json'), function (err, default_categories) { + if (err) { + return next(err); + } + default_categories = JSON.parse(default_categories); + + async.eachSeries(default_categories, Categories.create, next); + }); }); } diff --git a/src/logger.js b/src/logger.js index 90926dd5fd..f1da65555e 100644 --- a/src/logger.js +++ b/src/logger.js @@ -10,7 +10,8 @@ var fs = require('fs'), winston = require('winston'), util = require('util'), socketio = require('socket.io'), - meta = require('./meta'); + meta = require('./meta'), + morgan = require('morgan'); var opts = { /* @@ -124,7 +125,7 @@ var opts = { /* * Always initialize "ofn" (original function) with the original logger function */ - opts.express.ofn = express.logger({stream : opts.streams.log.f}); + opts.express.ofn = morgan({stream : opts.streams.log.f}); }; Logger.expressLogger = function(req,res,next) { diff --git a/src/middleware/index.js b/src/middleware/index.js index 844e46cdd8..6c59c1709a 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -15,6 +15,13 @@ var utils = require('./../../public/src/utils'), winston = require('winston'), flash = require('connect-flash'), templates = require('templates.js'), + bodyParser = require('body-parser'), + cookieParser = require('cookie-parser'), + compression = require('compression'), + favicon = require('serve-favicon'), + multipart = require('connect-multiparty'), + csrf = require('csurf'), + session = require('express-session'), relativePath, viewsPath, @@ -134,52 +141,6 @@ function compileTemplates(pluginTemplates) { }); } -function handleErrors(err, req, res, next) { - // we may use properties of the error object - // here and next(err) appropriately, or if - // we possibly recovered from the error, simply next(). - console.error(err.stack); - - var status = err.status || 500; - res.status(status); - - req.flash('errorMessage', err.message); - - res.redirect('500'); -} - -function catch404(req, res, next) { - var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'), - isClientScript = new RegExp('^' + relativePath + '\\/src\\/forum(\\/admin)?\\/.+\\.js'); - - res.status(404); - - if (isClientScript.test(req.url)) { - res.type('text/javascript').send(200, ''); - } else if (isLanguage.test(req.url)) { - res.json(200, {}); - } else if (req.accepts('html')) { - if (process.env.NODE_ENV === 'development') { - winston.warn('Route requested but not found: ' + req.url); - } - - res.redirect(relativePath + '/404'); - } else if (req.accepts('json')) { - if (process.env.NODE_ENV === 'development') { - winston.warn('Route requested but not found: ' + req.url); - } - - res.json({ - error: 'Not found' - }); - } else { - res.type('txt').send('Not found'); - } -} - - - - module.exports = function(app, data) { middleware = require('./middleware')(app); @@ -188,65 +149,61 @@ module.exports = function(app, data) { themesPath = nconf.get('themes_path'); baseTemplatesPath = nconf.get('base_templates_path'); - app.configure(function() { - app.engine('tpl', templates.__express); - app.set('view engine', 'tpl'); - app.set('views', viewsPath); - app.use(flash()); - app.enable('view cache'); + app.engine('tpl', templates.__express); + app.set('view engine', 'tpl'); + app.set('views', viewsPath); + app.set('json spaces', process.env.NODE_ENV === 'development' ? 4 : 0); + app.use(flash()); - app.use(express.compress()); + app.enable('view cache'); - app.use(express.favicon(path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico'))); - app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon); - - app.use(express.bodyParser()); - app.use(express.cookieParser()); - - app.use(express.session({ - store: db.sessionStore, - secret: nconf.get('secret'), - key: 'express.sid', - cookie: { - maxAge: 1000 * 60 * 60 * 24 * parseInt(meta.configs.loginDays || 14, 10) - } - })); + app.use(compression()); - app.use(express.csrf()); // todo, make this a conditional middleware + app.use(favicon(path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico'))); + app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon); - app.use(function (req, res, next) { - res.locals.csrf_token = req.session._csrf; - res.setHeader('X-Powered-By', 'NodeBB'); + app.use(bodyParser.urlencoded({extended: true})); + app.use(bodyParser.json()); + app.use(cookieParser()); - res.setHeader('X-Frame-Options', 'SAMEORIGIN'); - if (meta.config['allow-from-uri']) { - res.setHeader('ALLOW-FROM', meta.config['allow-from-uri']); - } + app.use(session({ + store: db.sessionStore, + secret: nconf.get('secret'), + key: 'express.sid', + cookie: { + maxAge: 1000 * 60 * 60 * 24 * parseInt(meta.configs.loginDays || 14, 10) + }, + resave: true, + saveUninitialized: true + })); - next(); - }); + app.use(multipart()); + app.use(csrf()); - app.use(middleware.processRender); + app.use(function (req, res, next) { + res.locals.csrf_token = req.csrfToken(); + res.setHeader('X-Powered-By', 'NodeBB'); - auth.initialize(app); + res.setHeader('X-Frame-Options', 'SAMEORIGIN'); + if (meta.config['allow-from-uri']) { + res.setHeader('ALLOW-FROM', meta.config['allow-from-uri']); + } - routeCurrentTheme(app, data.currentThemeId, data.themesData); - routeThemeScreenshots(app, data.themesData); + next(); + }); - plugins.getTemplates(function(err, pluginTemplates) { - compileTemplates(pluginTemplates); - }); + app.use(middleware.processRender); - app.use(relativePath, app.router); + auth.initialize(app); - app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), { - maxAge: app.enabled('cache') ? 5184000000 : 0 - })); + routeCurrentTheme(app, data.currentThemeId, data.themesData); + routeThemeScreenshots(app, data.themesData); - app.use(catch404); - app.use(handleErrors); + plugins.getTemplates(function(err, pluginTemplates) { + compileTemplates(pluginTemplates); }); + return middleware; }; diff --git a/src/plugins.js b/src/plugins.js index 689ea1e026..f35cc2e2a9 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -73,7 +73,7 @@ var fs = require('fs'), }, function(plugins, next) { if (!plugins || !Array.isArray(plugins)) { - next(); + return next(); } plugins.push(meta.config['theme:id']); diff --git a/src/postTools.js b/src/postTools.js index b7361f14ee..97d3e71928 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -54,7 +54,7 @@ var winston = require('winston'), if (err) { return next(err); } - + options.tags = options.tags || []; if (isMainPost) { title = title.trim(); diff --git a/src/routes/admin.js b/src/routes/admin.js index 3928298ada..8b6b06468c 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -2,7 +2,7 @@ function mainRoutes(app, middleware, controllers) { - app.get('/admin/', middleware.admin.buildHeader, controllers.admin.home); + app.get('/admin', middleware.admin.buildHeader, controllers.admin.home); app.get('/admin/index', middleware.admin.buildHeader, controllers.admin.home); app.get('/api/admin/index', controllers.admin.home); diff --git a/src/routes/api.js b/src/routes/api.js index 001cb5a01b..9af798e95b 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -4,6 +4,7 @@ var path = require('path'), async = require('async'), fs = require('fs'), nconf = require('nconf'), + express = require('express'), user = require('../user'), topics = require('../topics'), @@ -178,17 +179,19 @@ function getRecentPosts(req, res, next) { } module.exports = function(app, middleware, controllers) { - app.namespace('/api', function () { - app.get('/config', controllers.api.getConfig); - app.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.accounts.getUserByUID); - app.get('/get_templates_listing', getTemplatesListing); - app.get('/categories/:cid/moderators', getModerators); - app.get('/recent/posts/:term?', getRecentPosts); + var router = express.Router(); + app.use('/api', router); - app.post('/post/upload', uploadPost); - app.post('/topic/thumb/upload', uploadThumb); - app.post('/user/:userslug/uploadpicture', middleware.authenticate, middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, controllers.accounts.uploadPicture); - }); + router.get('/config', controllers.api.getConfig); + + router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.accounts.getUserByUID); + router.get('/get_templates_listing', getTemplatesListing); + router.get('/categories/:cid/moderators', getModerators); + router.get('/recent/posts/:term?', getRecentPosts); + + router.post('/post/upload', uploadPost); + router.post('/topic/thumb/upload', uploadThumb); + router.post('/user/:userslug/uploadpicture', middleware.authenticate, middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, controllers.accounts.uploadPicture); }; diff --git a/src/routes/debug.js b/src/routes/debug.js index ccaa4209a6..a03c5b2649 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -1,60 +1,61 @@ "use strict"; -var user = require('./../user'), +var express = require('express'), + user = require('./../user'), categories = require('./../categories'), topics = require('./../topics'), posts = require('./../posts'); module.exports = function(app, middleware, controllers) { - app.namespace('/debug', function() { - app.get('/uid/:uid', function (req, res) { - if (!req.params.uid) { - return res.redirect('/404'); - } + var router = express.Router(); + app.use('/debug', router); + router.get('/uid/:uid', function (req, res) { + if (!req.params.uid) { + return res.redirect('/404'); + } - user.getUserData(req.params.uid, function (err, data) { - if (data) { - res.send(data); - } else { - res.json(404, { - error: "User doesn't exist!" - }); - } - }); + user.getUserData(req.params.uid, function (err, data) { + if (data) { + res.send(data); + } else { + res.json(404, { + error: "User doesn't exist!" + }); + } }); + }); - app.get('/cid/:cid', function (req, res) { - categories.getCategoryData(req.params.cid, function (err, data) { - if (data) { - res.send(data); - } else { - res.send(404, "Category doesn't exist!"); - } - }); + router.get('/cid/:cid', function (req, res) { + categories.getCategoryData(req.params.cid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Category doesn't exist!"); + } }); + }); - app.get('/tid/:tid', function (req, res) { - topics.getTopicData(req.params.tid, function (err, data) { - if (data) { - res.send(data); - } else { - res.send(404, "Topic doesn't exist!"); - } - }); + router.get('/tid/:tid', function (req, res) { + topics.getTopicData(req.params.tid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Topic doesn't exist!"); + } }); + }); - app.get('/pid/:pid', function (req, res) { - posts.getPostData(req.params.pid, function (err, data) { - if (data) { - res.send(data); - } else { - res.send(404, "Post doesn't exist!"); - } - }); + router.get('/pid/:pid', function (req, res) { + posts.getPostData(req.params.pid, function (err, data) { + if (data) { + res.send(data); + } else { + res.send(404, "Post doesn't exist!"); + } }); + }); - app.get('/test', function(req, res) { - res.redirect('404'); - }); + router.get('/test', function(req, res) { + res.redirect('404'); }); }; diff --git a/src/routes/index.js b/src/routes/index.js index 9166d340c1..bea9e769dd 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,9 +1,12 @@ "use strict"; var nconf = require('nconf'), + path = require('path'), + winston = require('winston'), controllers = require('./../controllers'), meta = require('./../meta'), plugins = require('./../plugins'), + express = require('express'), metaRoutes = require('./meta'), apiRoutes = require('./api'), @@ -74,8 +77,8 @@ function categoryRoutes(app, middleware, controllers) { app.get('/recent/:term?', middleware.buildHeader, controllers.categories.recent); app.get('/api/recent/:term?', controllers.categories.recent); - app.get('/unread/', middleware.buildHeader, middleware.authenticate, controllers.categories.unread); - app.get('/api/unread/', middleware.authenticate, controllers.categories.unread); + app.get('/unread', middleware.buildHeader, middleware.authenticate, controllers.categories.unread); + app.get('/api/unread', middleware.authenticate, controllers.categories.unread); app.get('/api/unread/total', middleware.authenticate, controllers.categories.unreadTotal); @@ -151,38 +154,93 @@ function groupRoutes(app, middleware, controllers) { module.exports = function(app, middleware) { - app.namespace(nconf.get('relative_path'), function() { - plugins.ready(function() { - app.all('/api/*', middleware.updateLastOnlineTime, middleware.prepareAPI); - app.all('/api/admin/*', middleware.admin.isAdmin, middleware.prepareAPI); - app.all('/admin/*', middleware.admin.isAdmin); - app.get('/admin', middleware.admin.isAdmin); - - plugins.fireHook('action:app.load', app, middleware, controllers); - - adminRoutes(app, middleware, controllers); - metaRoutes(app, middleware, controllers); - apiRoutes(app, middleware, controllers); - feedRoutes(app, middleware, controllers); - pluginRoutes(app, middleware, controllers); - authRoutes.createRoutes(app, middleware, controllers); - - /** - * Every view has an associated API route. - * - */ - mainRoutes(app, middleware, controllers); - staticRoutes(app, middleware, controllers); - topicRoutes(app, middleware, controllers); - tagRoutes(app, middleware, controllers); - categoryRoutes(app, middleware, controllers); - accountRoutes(app, middleware, controllers); - userRoutes(app, middleware, controllers); - groupRoutes(app, middleware, controllers); - }); - if (process.env.NODE_ENV === 'development') { - require('./debug')(app, middleware, controllers); - } + var router = express.Router(); + app.use(nconf.get('relative_path'), router); + + plugins.ready(function() { + + router.all('/api/*', middleware.updateLastOnlineTime, middleware.prepareAPI); + router.all('/api/admin/*', middleware.admin.isAdmin, middleware.prepareAPI); + router.all('/admin/*', middleware.admin.isAdmin); + router.get('/admin', middleware.admin.isAdmin); + + plugins.fireHook('action:app.load', router, middleware, controllers); + + adminRoutes(router, middleware, controllers); + metaRoutes(router, middleware, controllers); + apiRoutes(router, middleware, controllers); + feedRoutes(router, middleware, controllers); + pluginRoutes(router, middleware, controllers); + authRoutes.createRoutes(router, middleware, controllers); + + /** + * Every view has an associated API route. + * + */ + mainRoutes(router, middleware, controllers); + staticRoutes(router, middleware, controllers); + topicRoutes(router, middleware, controllers); + tagRoutes(router, middleware, controllers); + categoryRoutes(router, middleware, controllers); + accountRoutes(router, middleware, controllers); + userRoutes(router, middleware, controllers); + groupRoutes(router, middleware, controllers); + + + app.use(nconf.get('relative_path'), express.static(path.join(__dirname, '../../', 'public'), { + maxAge: app.enabled('cache') ? 5184000000 : 0 + })); + app.use(catch404); + app.use(handleErrors); }); + + if (process.env.NODE_ENV === 'development') { + require('./debug')(app, middleware, controllers); + } + }; + +function handleErrors(err, req, res, next) { + // we may use properties of the error object + // here and next(err) appropriately, or if + // we possibly recovered from the error, simply next(). + console.error(err.stack); + + var status = err.status || 500; + res.status(status); + + req.flash('errorMessage', err.message); + + res.redirect('500'); +} + +function catch404(req, res, next) { + var relativePath = nconf.get('relative_path'); + var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'), + isClientScript = new RegExp('^' + relativePath + '\\/src\\/forum(\\/admin)?\\/.+\\.js'); + + res.status(404); + + if (isClientScript.test(req.url)) { + res.type('text/javascript').send(200, ''); + } else if (isLanguage.test(req.url)) { + res.json(200, {}); + } else if (req.accepts('html')) { + if (process.env.NODE_ENV === 'development') { + winston.warn('Route requested but not found: ' + req.url); + } + + res.redirect(relativePath + '/404'); + } else if (req.accepts('json')) { + if (process.env.NODE_ENV === 'development') { + winston.warn('Route requested but not found: ' + req.url); + } + + res.json({ + error: 'Not found' + }); + } else { + res.type('txt').send('Not found'); + } +} \ No newline at end of file diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 3b872c2cac..750b8eeb11 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -7,8 +7,7 @@ var SocketIO = require('socket.io'), path = require('path'), fs = require('fs'), nconf = require('nconf'), - express = require('express'), - socketCookieParser = express.cookieParser(nconf.get('secret')), + socketCookieParser = require('cookie-parser')(nconf.get('secret')), winston = require('winston'), db = require('../database'), @@ -60,7 +59,7 @@ Sockets.init = function(server) { winston.error(err.message); } - sessionID = socket.handshake.signedCookies["express.sid"]; + sessionID = socket.handshake.signedCookies['express.sid']; db.sessionStore.get(sessionID, function(err, sessionData) { if (!err && sessionData && sessionData.passport && sessionData.passport.user) { uid = parseInt(sessionData.passport.user, 10); diff --git a/src/webserver.js b/src/webserver.js index e7c19359f0..9b50f0b553 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -2,7 +2,6 @@ var path = require('path'), fs = require('fs'), nconf = require('nconf'), express = require('express'), - express_namespace = require('express-namespace'), WebServer = express(), server, winston = require('winston'),