diff --git a/src/middleware/index.js b/src/middleware/index.js index d5fb9b6a82..4c148a015c 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -16,9 +16,7 @@ var templates = require('./../../public/src/templates'), var middleware = {}; -/* -* Helper functions -*/ + function routeThemeScreenshots(app, themes) { var screenshotPath; @@ -170,6 +168,9 @@ function catch404(req, res, next) { } } + + + module.exports = function(app, data) { middleware = require('./middleware')(app); diff --git a/src/routes/admin.js b/src/routes/admin.js index 521822d315..fa72013e79 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -22,7 +22,6 @@ var nconf = require('nconf'), utils = require('./../../public/src/utils'), templates = require('./../../public/src/templates'); -var Admin = {}; function uploadImage(filename, req, res) { function done(err, image) { @@ -45,6 +44,89 @@ function uploadImage(filename, req, res) { } } +function uploadCategoryPicture(req, res, next) { + if (!req.user) { + return res.redirect('/403'); + } + + var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], + params = null, er; + try { + params = JSON.parse(req.body.params); + } catch (e) { + er = { + error: 'Error uploading file! Error :' + e.message + }; + return res.send(req.xhr ? er : JSON.stringify(er)); + } + + if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { + er = { + error: 'Allowed image types are png, jpg and gif!' + }; + res.send(req.xhr ? er : JSON.stringify(er)); + return; + } + + var filename = 'category-' + params.cid + path.extname(req.files.userPhoto.name); + + uploadImage(filename, req, res); +} + +function uploadFavicon(req, res, next) { + if (!req.user) { + return res.redirect('/403'); + } + + var allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'], + er; + + if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { + er = {error: 'You can only upload icon file type!'}; + res.send(req.xhr ? er : JSON.stringify(er)); + return; + } + + file.saveFileToLocal('favicon.ico', req.files.userPhoto.path, function(err, image) { + fs.unlink(req.files.userPhoto.path); + + if(err) { + er = {error: err.message}; + return res.send(req.xhr ? er : JSON.stringify(er)); + } + + var rs = {path: image.url}; + res.send(req.xhr ? rs : JSON.stringify(rs)); + }); +} + +function uploadLogo(req, res, next) { + if (!req.user) { + return res.redirect('/403'); + } + + var allowedTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif'], + er; + + if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { + er = {error: 'Allowed image types are png, jpg and gif!'}; + res.send(req.xhr ? er : JSON.stringify(er)); + return; + } + + var filename = 'site-logo' + path.extname(req.files.userPhoto.name); + + uploadImage(filename, req, res); +} + +function getUsersCSV(req, res, next) { + user.getUsersCSV(function(err, data) { + res.attachment('users.csv'); + res.setHeader('Content-Type', 'text/csv'); + res.end(data); + }); +} + module.exports = function(app, middleware, controllers) { app.all('/api/admin/*', middleware.admin.isAdmin, middleware.prepareAPI); app.all('/admin/*', middleware.admin.isAdmin); @@ -104,129 +186,11 @@ module.exports = function(app, middleware, controllers) { app.get('/api/admin/groups', controllers.admin.groups.get); - - app.namespace('/admin', function () { - app.post('/category/uploadpicture', function(req, res) { - if (!req.user) { - return res.redirect('/403'); - } - - var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], - params = null, er; - try { - params = JSON.parse(req.body.params); - } catch (e) { - er = { - error: 'Error uploading file! Error :' + e.message - }; - return res.send(req.xhr ? er : JSON.stringify(er)); - } - - if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { - er = { - error: 'Allowed image types are png, jpg and gif!' - }; - res.send(req.xhr ? er : JSON.stringify(er)); - return; - } - - var filename = 'category-' + params.cid + path.extname(req.files.userPhoto.name); - - uploadImage(filename, req, res); - }); - - app.post('/uploadfavicon', function(req, res) { - if (!req.user) { - return res.redirect('/403'); - } - - var allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'], - er; - - if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { - er = {error: 'You can only upload icon file type!'}; - res.send(req.xhr ? er : JSON.stringify(er)); - return; - } - - file.saveFileToLocal('favicon.ico', req.files.userPhoto.path, function(err, image) { - fs.unlink(req.files.userPhoto.path); - - if(err) { - er = {error: err.message}; - return res.send(req.xhr ? er : JSON.stringify(er)); - } - - var rs = {path: image.url}; - res.send(req.xhr ? rs : JSON.stringify(rs)); - }); - }); - - app.post('/uploadlogo', function(req, res) { - - if (!req.user) { - return res.redirect('/403'); - } - - var allowedTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif'], - er; - - if (allowedTypes.indexOf(req.files.userPhoto.type) === -1) { - er = {error: 'Allowed image types are png, jpg and gif!'}; - res.send(req.xhr ? er : JSON.stringify(er)); - return; - } - - var filename = 'site-logo' + path.extname(req.files.userPhoto.name); - - uploadImage(filename, req, res); - }); - - app.get('/users/csv', function(req, res) { - user.getUsersCSV(function(err, data) { - res.attachment('users.csv'); - res.setHeader('Content-Type', 'text/csv'); - res.end(data); - }); - }); - }); + app.get('/users/csv', getUsersCSV); - var custom_routes = { - 'routes': [], - 'api': [] - }; - - plugins.ready(function() { - plugins.fireHook('filter:admin.create_routes', custom_routes, function(err, custom_routes) { - var route, routes = custom_routes.routes; - - for (route in routes) { - if (routes.hasOwnProperty(route)) { - (function(route) { - app[routes[route].method || 'get']('/admin' + routes[route].route, function(req, res) { - routes[route].options(req, res, function(options) { - Admin.buildHeader(req, res, function (err, header) { - res.send(header + options.content + templates['admin/footer']); - }); - }); - }); - }(route)); - } - } - - var apiRoutes = custom_routes.api; - for (route in apiRoutes) { - if (apiRoutes.hasOwnProperty(route)) { - (function(route) { - app[apiRoutes[route].method || 'get']('/api/admin' + apiRoutes[route].route, function(req, res) { - apiRoutes[route].callback(req, res, function(data) { - res.json(data); - }); - }); - }(route)); - } - } - }); + app.post('/category/uploadpicture', uploadCategoryPicture); + app.post('/uploadfavicon', uploadFavicon); + app.post('/uploadlogo', uploadLogo); }); }; diff --git a/src/routes/api.js b/src/routes/api.js index 575a91bad6..d7b0efb6db 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -13,152 +13,156 @@ var path = require('path'), meta = require('./../meta'), plugins = require('./../plugins'), utils = require('./../../public/src/utils'), - pkg = require('./../../package.json'); + pkg = require('./../../package.json'), + customTemplates = []; -module.exports = function(app, middleware, controllers) { - app.namespace('/api', function () { - app.all('*', middleware.updateLastOnlineTime, middleware.prepareAPI); - app.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.accounts.getUserByUID); +function searchTerm(req, res, next) { + if (!plugins.hasListeners('filter:search.query')) { + return res.redirect('/404'); + } - app.get('/get_templates_listing', function (req, res) { - utils.walk(nconf.get('views_dir'), function (err, data) { - data = data.concat(app.get_custom_templates()) - .filter(function(value, index, self) { - return self.indexOf(value) === index; - }).map(function(el) { - return el.replace(nconf.get('views_dir') + '/', ''); - }); + function searchPosts(callback) { + plugins.fireHook('filter:search.query', { + index: 'post', + query: req.params.term + }, function(err, pids) { + if (err) { + return callback(err); + } - res.json(data); - }); + posts.getPostSummaryByPids(pids, false, callback); }); - - app.get('/config', controllers.api.getConfig); - - app.get('/search/:term', function (req, res, next) { - if (!plugins.hasListeners('filter:search.query')) { - return res.redirect('/404'); + } + + function searchTopics(callback) { + plugins.fireHook('filter:search.query', { + index: 'topic', + query: req.params.term + }, function(err, tids) { + if (err) { + return callback(err); } - function searchPosts(callback) { - plugins.fireHook('filter:search.query', { - index: 'post', - query: req.params.term - }, function(err, pids) { - if (err) { - return callback(err); - } + topics.getTopicsByTids(tids, 0, callback); + }); + } - posts.getPostSummaryByPids(pids, false, callback); - }); + if ((req.user && req.user.uid) || meta.config.allowGuestSearching === '1') { + async.parallel([searchPosts, searchTopics], function (err, results) { + if (err) { + return next(err); } - function searchTopics(callback) { - plugins.fireHook('filter:search.query', { - index: 'topic', - query: req.params.term - }, function(err, tids) { - if (err) { - return callback(err); - } - - topics.getTopicsByTids(tids, 0, callback); - }); + if(!results) { + results = []; + results[0] = results[1] = []; } - if ((req.user && req.user.uid) || meta.config.allowGuestSearching === '1') { - async.parallel([searchPosts, searchTopics], function (err, results) { - if (err) { - return next(err); - } - - if(!results) { - results = []; - results[0] = results[1] = []; - } - - return res.json({ - show_no_topics: results[1].length ? 'hide' : '', - show_no_posts: results[0].length ? 'hide' : '', - show_results: '', - search_query: req.params.term, - posts: results[0], - topics: results[1], - post_matches : results[0].length, - topic_matches : results[1].length - }); - }); - } else { - res.send(403); - } + return res.json({ + show_no_topics: results[1].length ? 'hide' : '', + show_no_posts: results[0].length ? 'hide' : '', + show_results: '', + search_query: req.params.term, + posts: results[0], + topics: results[1], + post_matches : results[0].length, + topic_matches : results[1].length + }); }); + } else { + res.send(403); + } +} + +function upload(req, res, filesIterator, next) { + if(!req.user) { + return res.json(403, {message:'not allowed'}); + } + var files = req.files.files; + + if(!Array.isArray(files)) { + return res.json(500, {message: 'invalid files'}); + } + + if(Array.isArray(files[0])) { + files = files[0]; + } + + function deleteTempFiles() { + for(var i=0; i 0) { - feed.pubDate = new Date(parseInt(topicData.posts[0].timestamp, 10)).toUTCString(); - } + var feed = new rss({ + title: topicData.title, + description: description, + feed_url: nconf.get('url') + '/topic/' + tid + '.rss', + site_url: nconf.get('url') + '/topic/' + topicData.slug, + image_url: image_url, + author: author, + ttl: 60 + }), + dateStamp; - topicData.posts.forEach(function(postData) { - if (parseInt(postData.deleted, 10) === 0) { - dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString(); - - feed.item({ - title: 'Reply to ' + topicData.title + ' on ' + dateStamp, - description: postData.content, - url: nconf.get('url') + '/topic/' + topicData.slug + '#' + postData.pid, - author: postData.username, - date: dateStamp - }); - } - }); + // Add pubDate if topic contains posts + if (topicData.posts.length > 0) { + feed.pubDate = new Date(parseInt(topicData.posts[0].timestamp, 10)).toUTCString(); + } - var xml = feed.xml(); - res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); - }); + topicData.posts.forEach(function(postData) { + if (parseInt(postData.deleted, 10) === 0) { + dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString(); - }; - - function generateForCategory(req, res, next) { - var cid = req.params.category_id; - - categories.getCategoryById(cid, 0, 25, 0, function (err, categoryData) { - if (err) { - return next(err); - } - - var feed = new rss({ - title: categoryData.name, - description: categoryData.description, - feed_url: nconf.get('url') + '/category/' + cid + '.rss', - site_url: nconf.get('url') + '/category/' + categoryData.cid, - ttl: 60 - }); - - // Add pubDate if category has topics - if (categoryData.topics.length > 0) feed.pubDate = new Date(parseInt(categoryData.topics[0].lastposttime, 10)).toUTCString(); - - categoryData.topics.forEach(function(topicData) { feed.item({ - title: topicData.title, - url: nconf.get('url') + '/topic/' + topicData.slug, - author: topicData.username, - date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() + title: 'Reply to ' + topicData.title + ' on ' + dateStamp, + description: postData.content, + url: nconf.get('url') + '/topic/' + topicData.slug + '#' + postData.pid, + author: postData.username, + date: dateStamp }); - }); - - var xml = feed.xml(); - res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + } }); - }; - function generateForRecent(req, res, next) { - topics.getLatestTopics(0, 0, 19, 'month', function (err, recentData) { - if(err){ - return next(err); - } + var xml = feed.xml(); + res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + }); - var feed = new rss({ - title: 'Recently Active Topics', - description: 'A list of topics that have been active within the past 24 hours', - feed_url: nconf.get('url') + '/recent.rss', - site_url: nconf.get('url') + '/recent', - ttl: 60 - }); +} - // Add pubDate if recent topics list contains topics - if (recentData.topics.length > 0) { - feed.pubDate = new Date(parseInt(recentData.topics[0].lastposttime, 10)).toUTCString(); - } +function generateForCategory(req, res, next) { + var cid = req.params.category_id; - recentData.topics.forEach(function(topicData) { - feed.item({ - title: topicData.title, - url: nconf.get('url') + '/topic/' + topicData.slug, - author: topicData.username, - date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() - }); + categories.getCategoryById(cid, 0, 25, 0, function (err, categoryData) { + if (err) { + return next(err); + } + + var feed = new rss({ + title: categoryData.name, + description: categoryData.description, + feed_url: nconf.get('url') + '/category/' + cid + '.rss', + site_url: nconf.get('url') + '/category/' + categoryData.cid, + ttl: 60 }); - var xml = feed.xml(); - res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + // Add pubDate if category has topics + if (categoryData.topics.length > 0) { + feed.pubDate = new Date(parseInt(categoryData.topics[0].lastposttime, 10)).toUTCString(); + } + + categoryData.topics.forEach(function(topicData) { + feed.item({ + title: topicData.title, + url: nconf.get('url') + '/topic/' + topicData.slug, + author: topicData.username, + date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() + }); }); - }; - - function generateForPopular(req, res, next) { - topics.getTopicsFromSet(0, 'topics:posts', 0, 19, function (err, popularData) { - if(err){ - return next(err); - } - var feed = new rss({ - title: 'Popular Topics', - description: 'A list of topics that are sorted by post count', - feed_url: nconf.get('url') + '/popular.rss', - site_url: nconf.get('url') + '/popular', - ttl: 60 - }); + var xml = feed.xml(); + res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + }); +} + +function generateForRecent(req, res, next) { + topics.getLatestTopics(0, 0, 19, 'month', function (err, recentData) { + if(err){ + return next(err); + } + + var feed = new rss({ + title: 'Recently Active Topics', + description: 'A list of topics that have been active within the past 24 hours', + feed_url: nconf.get('url') + '/recent.rss', + site_url: nconf.get('url') + '/recent', + ttl: 60 + }); - // Add pubDate if recent topics list contains topics - if (popularData.topics.length > 0) { - feed.pubDate = new Date(parseInt(popularData.topics[0].lastposttime, 10)).toUTCString(); - } + // Add pubDate if recent topics list contains topics + if (recentData.topics.length > 0) { + feed.pubDate = new Date(parseInt(recentData.topics[0].lastposttime, 10)).toUTCString(); + } + + recentData.topics.forEach(function(topicData) { + feed.item({ + title: topicData.title, + url: nconf.get('url') + '/topic/' + topicData.slug, + author: topicData.username, + date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() + }); + }); - popularData.topics.forEach(function(topicData) { - feed.item({ - title: topicData.title, - url: nconf.get('url') + '/topic/' + topicData.slug, - author: topicData.username, - date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() - }); + var xml = feed.xml(); + res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + }); +} + +function generateForPopular(req, res, next) { + topics.getTopicsFromSet(0, 'topics:posts', 0, 19, function (err, popularData) { + if(err){ + return next(err); + } + + var feed = new rss({ + title: 'Popular Topics', + description: 'A list of topics that are sorted by post count', + feed_url: nconf.get('url') + '/popular.rss', + site_url: nconf.get('url') + '/popular', + ttl: 60 }); - var xml = feed.xml(); - res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + // Add pubDate if recent topics list contains topics + if (popularData.topics.length > 0) { + feed.pubDate = new Date(parseInt(popularData.topics[0].lastposttime, 10)).toUTCString(); + } + + popularData.topics.forEach(function(topicData) { + feed.item({ + title: topicData.title, + url: nconf.get('url') + '/topic/' + topicData.slug, + author: topicData.username, + date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() + }); }); - }; -}(exports)); + + var xml = feed.xml(); + res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); + }); +} + +module.exports = function(app, middleware, controllers){ + app.get('/topic/:topic_id.rss', hasTopicPrivileges, generateForTopic); + app.get('/category/:category_id.rss', hasCategoryPrivileges, generateForCategory); + app.get('/recent.rss', generateForRecent); + app.get('/popular.rss', generateForPopular); +}; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index d10f94c0a6..4c039e6f29 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -5,20 +5,19 @@ var nconf = require('nconf'), meta = require('./../meta'), plugins = require('./../plugins'), - metaRoute = require('./meta'), - apiRoute = require('./api'), - admin = require('./admin'), - feedsRoute = require('./feeds'); + metaRoutes = require('./meta'), + apiRoutes = require('./api'), + adminRoutes = require('./admin'), + feedRoutes = require('./feeds'), + pluginRoutes = require('./plugins'); module.exports = function(app, middleware) { app.namespace(nconf.get('relative_path'), function() { - //temp - metaRoute.createRoutes(app); - feedsRoute.createRoutes(app); - - admin(app, middleware, controllers); - apiRoute(app, middleware, controllers); + adminRoutes(app, middleware, controllers); + metaRoutes(app, middleware, controllers); + apiRoutes(app, middleware, controllers); + feedRoutes(app, middleware, controllers); /** * Every view has an associated API route. @@ -120,85 +119,12 @@ module.exports = function(app, middleware) { app.get('/api/users/latest', middleware.checkGlobalPrivacySettings, controllers.users.getUsersSortedByJoinDate); app.get('/users/search', middleware.buildHeader, middleware.checkGlobalPrivacySettings, controllers.users.getUsersForSearch); - app.get('/api/users/search', middleware.checkGlobalPrivacySettings, controllers.users.getUsersForSearch); - - /* Misc */ - app.get('/sitemap.xml', controllers.sitemap); - app.get('/robots.txt', controllers.robots); + app.get('/api/users/search', middleware.checkGlobalPrivacySettings, controllers.users.getUsersForSearch); + pluginRoutes(app, middleware, controllers); - - // Other routes - require('./plugins')(app); - - // Debug routes if (process.env.NODE_ENV === 'development') { - require('./debug')(app); + require('./debug')(app, middleware, controllers); } - - var custom_routes = { - 'routes': [], - 'api': [], - 'templates': [] - }; - - app.get_custom_templates = function() { - return custom_routes.templates.map(function(tpl) { - return tpl.template; - }); - }; - - - plugins.ready(function() { - /* - * TO BE DEPRECATED post 0.4x - */ - plugins.fireHook('filter:server.create_routes', custom_routes, function(err, custom_routes) { - var route, - routes = custom_routes.routes; - - for (route in routes) { - if (routes.hasOwnProperty(route)) { - (function(route) { - app[routes[route].method || 'get'](routes[route].route, function(req, res) { - routes[route].options(req, res, function(options) { - app.build_header({ - req: options.req || req, - res: options.res || res - }, function (err, header) { - //res.send(header + options.content + templates.footer); - }); - }); - }); - }(route)); - } - } - - var apiRoutes = custom_routes.api; - for (route in apiRoutes) { - if (apiRoutes.hasOwnProperty(route)) { - (function(route) { - app[apiRoutes[route].method || 'get']('/api' + apiRoutes[route].route, function(req, res) { - apiRoutes[route].callback(req, res, function(data) { - res.json(data); - }); - }); - }(route)); - } - } - - var templateRoutes = custom_routes.templates; - for (route in templateRoutes) { - if (templateRoutes.hasOwnProperty(route)) { - (function(route) { - app.get('/templates/' + templateRoutes[route].template, function(req, res) { - res.send(templateRoutes[route].content); - }); - }(route)); - } - } - - }); - }); }); }; \ No newline at end of file diff --git a/src/routes/meta.js b/src/routes/meta.js index cc5683b924..eb25ba31f0 100644 --- a/src/routes/meta.js +++ b/src/routes/meta.js @@ -1,67 +1,78 @@ +"use strict"; + var path = require('path'), nconf = require('nconf'), less = require('less'), meta = require('../meta'), db = require('../database'), - plugins = require('../plugins'); + plugins = require('../plugins'), -(function (Meta) { - Meta.createRoutes = function(app) { - app.get('/stylesheet.css', function(req, res) { - if (meta.css.cache) { - res.type('text/css').send(200, meta.css.cache); - return; - } + minificationEnabled = false; + + +function sendMinifiedJS(req, res, next) { + function sendCached() { + return res.type('text/javascript').send(meta.js.cache); + } - db.getObjectFields('config', ['theme:type', 'theme:id'], function(err, themeData) { - var themeId = (themeData['theme:id'] || 'nodebb-theme-vanilla'), - baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')), - paths = [baseThemePath, path.join(__dirname, '../../node_modules')], - source = '@import "./theme";', - x, numLESS; + if (meta.js.cache) { + sendCached(); + } else { + if (minificationEnabled) { + meta.js.minify(function() { + sendCached(); + }); + } else { + // Compress only + meta.js.concatenate(function() { + sendCached(); + }); + } + } +} - // Add the imports for each LESS file - for(x=0,numLESS=plugins.lessFiles.length;x