From a05905f19677197a66abead23179afb5168440c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 26 Oct 2020 10:43:18 -0400 Subject: [PATCH] performance improvements (#8795) * perf: nconf/winston/render cache nconf.get calls modify middleware.pageView to call next earlier don't call winston.verbose on every hook see https://github.com/winstonjs/winston/issues/1669 translate header/footer separately and cache results for guests * fix: copy paste fail * refactor: style and fire hook only log in dev mode * fix: cache key, header changes based on template * perf: change replace * fix: add missing await * perf: category * perf: lodash clone * perf: remove escapeRegexChars --- public/src/modules/translator.js | 5 ++- public/src/utils.js | 4 +- src/analytics.js | 8 ++-- src/categories/index.js | 6 +-- src/categories/topics.js | 4 +- src/controllers/api.js | 18 ++++++--- src/controllers/category.js | 15 ++++--- src/controllers/helpers.js | 23 ++++++----- src/controllers/topics.js | 20 ++++++---- src/coverPhoto.js | 6 ++- src/database/mongo/hash.js | 52 ++++++++++--------------- src/database/redis/hash.js | 14 +++---- src/meta/errors.js | 3 +- src/meta/tags.js | 54 +++++++++++++------------ src/middleware/header.js | 4 +- src/middleware/headers.js | 1 - src/middleware/index.js | 21 +++++----- src/middleware/render.js | 67 ++++++++++++++++++++++---------- src/navigation/admin.js | 5 +-- src/navigation/index.js | 4 +- src/pagination.js | 2 +- src/plugins/hooks.js | 2 +- src/posts/uploads.js | 2 +- src/prestart.js | 5 +++ src/routes/helpers.js | 7 +++- src/socket.io/index.js | 10 +---- src/topics/index.js | 23 +++++------ src/topics/posts.js | 2 +- src/user/auth.js | 2 +- src/user/data.js | 10 +++-- src/webserver.js | 2 +- src/widgets/index.js | 4 +- test/mocks/databasemock.js | 10 +++-- test/user.js | 4 +- 34 files changed, 228 insertions(+), 191 deletions(-) diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 279e744648..2b9775b57e 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -493,7 +493,10 @@ * @returns {string} */ Translator.unescape = function unescape(text) { - return typeof text === 'string' ? text.replace(/[|\\\[/g, '[').replace(/]|\\\]/g, ']') : text; + return typeof text === 'string' ? + text.replace(/[/g, '[').replace(/\\\[/g, '[') + .replace(/]/g, ']').replace(/\\\]/g, ']') : + text; }; /** diff --git a/public/src/utils.js b/public/src/utils.js index 68cdeb2882..bb6e04f8da 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -324,9 +324,7 @@ }, // https://github.com/jprichardson/string.js/blob/master/lib/string.js stripHTMLTags: function (str, tags) { - var pattern = (tags || ['']).map(function (tag) { - return utils.escapeRegexChars(tag); - }).join('|'); + var pattern = (tags || ['']).join('|'); return String(str).replace(new RegExp('<(\\/)?(' + (pattern || '[^\\s>]+') + ')(\\s+[^<>]*?)?\\s*(\\/)?>', 'gi'), ''); }, diff --git a/src/analytics.js b/src/analytics.js index 1922f5d230..b3f706e0aa 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -14,6 +14,8 @@ const meta = require('./meta'); const Analytics = module.exports; +const secret = nconf.get('secret'); + const counters = {}; let pageViews = 0; @@ -64,10 +66,10 @@ Analytics.pageView = async function (payload) { if (payload.ip) { // Retrieve hash or calculate if not present - let hash = ipCache.get(payload.ip + nconf.get('secret')); + let hash = ipCache.get(payload.ip + secret); if (!hash) { - hash = crypto.createHash('sha1').update(payload.ip + nconf.get('secret')).digest('hex'); - ipCache.set(payload.ip + nconf.get('secret'), hash); + hash = crypto.createHash('sha1').update(payload.ip + secret).digest('hex'); + ipCache.set(payload.ip + secret, hash); } const score = await db.sortedSetScore('ip:recent', hash); diff --git a/src/categories/index.js b/src/categories/index.js index 9480c936bb..f13f7a7a2d 100644 --- a/src/categories/index.js +++ b/src/categories/index.js @@ -157,7 +157,7 @@ Categories.getTagWhitelist = async function (cids) { }); if (!nonCachedCids.length) { - return _.clone(cids.map(cid => cachedData[cid])); + return cids.map(cid => cachedData[cid]); } const keys = nonCachedCids.map(cid => 'cid:' + cid + ':tag:whitelist'); @@ -167,7 +167,7 @@ Categories.getTagWhitelist = async function (cids) { cachedData[cid] = data[index]; cache.set('cid:' + cid + ':tag:whitelist', data[index]); }); - return _.clone(cids.map(cid => cachedData[cid])); + return cids.map(cid => cachedData[cid]); }; function calculateTopicPostCount(category) { @@ -280,7 +280,7 @@ Categories.getTree = function (categories, parentCid) { if (cid) { categories[index].children = undefined; cidToCategory[cid] = categories[index]; - parents[cid] = _.clone(categories[index]); + parents[cid] = { ...categories[index] }; } }); diff --git a/src/categories/topics.js b/src/categories/topics.js index 5a39646c74..bf396b38bb 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -1,7 +1,5 @@ 'use strict'; -const _ = require('lodash'); - const db = require('../database'); const topics = require('../topics'); const plugins = require('../plugins'); @@ -25,7 +23,7 @@ module.exports = function (Categories) { }; Categories.getTopicIds = async function (data) { - const dataForPinned = _.cloneDeep(data); + const dataForPinned = { ...data }; dataForPinned.start = 0; dataForPinned.stop = -1; diff --git a/src/controllers/api.js b/src/controllers/api.js index e10c935d80..6407b01d32 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -15,11 +15,17 @@ const languages = require('../languages'); const apiController = module.exports; +const relative_path = nconf.get('relative_path'); +const upload_url = nconf.get('upload_url'); +const socketioTransports = nconf.get('socket.io:transports') || ['polling', 'websocket']; +const socketioOrigins = nconf.get('socket.io:origins'); +const websocketAddress = nconf.get('socket.io:address') || ''; + apiController.loadConfig = async function (req) { let config = { - relative_path: nconf.get('relative_path'), - upload_url: nconf.get('upload_url'), - assetBaseUrl: `${nconf.get('relative_path')}/assets`, + relative_path, + upload_url, + assetBaseUrl: `${relative_path}/assets`, siteTitle: validator.escape(String(meta.config.title || meta.config.browserTitle || 'NodeBB')), browserTitle: validator.escape(String(meta.config.browserTitle || meta.config.title || 'NodeBB')), titleLayout: (meta.config.titleLayout || '{pageTitle} | {browserTitle}').replace(/{/g, '{').replace(/}/g, '}'), @@ -40,9 +46,9 @@ apiController.loadConfig = async function (req) { disableChat: meta.config.disableChat === 1, disableChatMessageEditing: meta.config.disableChatMessageEditing === 1, maximumChatMessageLength: meta.config.maximumChatMessageLength || 1000, - socketioTransports: nconf.get('socket.io:transports') || ['polling', 'websocket'], - socketioOrigins: nconf.get('socket.io:origins'), - websocketAddress: nconf.get('socket.io:address') || '', + socketioTransports, + socketioOrigins, + websocketAddress, maxReconnectionAttempts: meta.config.maxReconnectionAttempts || 5, reconnectionDelay: meta.config.reconnectionDelay || 1500, topicsPerPage: meta.config.topicsPerPage || 20, diff --git a/src/controllers/category.js b/src/controllers/category.js index 5345749045..c19090a531 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -16,6 +16,9 @@ const analytics = require('../analytics'); const categoryController = module.exports; +const url = nconf.get('url'); +const relative_path = nconf.get('relative_path'); + categoryController.get = async function (req, res, next) { const cid = req.params.category_id; @@ -103,7 +106,7 @@ categoryController.get = async function (req, res, next) { categoryData.showSelect = userPrivileges.editable; categoryData.showTopicTools = userPrivileges.editable; categoryData.topicIndex = topicIndex; - categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss'; + categoryData.rssFeedUrl = url + '/category/' + categoryData.cid + '.rss'; if (parseInt(req.uid, 10)) { categories.markAsRead([cid], req.uid); categoryData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken; @@ -115,7 +118,7 @@ categoryController.get = async function (req, res, next) { categoryData['reputation:disabled'] = meta.config['reputation:disabled']; categoryData.pagination = pagination.create(currentPage, pageCount, req.query); categoryData.pagination.rel.forEach(function (rel) { - rel.href = nconf.get('url') + '/category/' + categoryData.slug + rel.href; + rel.href = url + '/category/' + categoryData.slug + rel.href; res.locals.linkTags.push(rel); }); @@ -128,12 +131,12 @@ async function buildBreadcrumbs(req, categoryData) { const breadcrumbs = [ { text: categoryData.name, - url: nconf.get('relative_path') + '/category/' + categoryData.slug, + url: relative_path + '/category/' + categoryData.slug, cid: categoryData.cid, }, ]; const crumbs = await helpers.buildCategoryBreadcrumbs(categoryData.parentCid); - if (req.originalUrl.startsWith(nconf.get('relative_path') + '/api/category') || req.originalUrl.startsWith(nconf.get('relative_path') + '/category')) { + if (req.originalUrl.startsWith(relative_path + '/api/category') || req.originalUrl.startsWith(relative_path + '/category')) { categoryData.breadcrumbs = crumbs.concat(breadcrumbs); } } @@ -160,7 +163,7 @@ function addTags(categoryData, res) { if (categoryData.backgroundImage) { if (!categoryData.backgroundImage.startsWith('http')) { - categoryData.backgroundImage = nconf.get('url') + categoryData.backgroundImage; + categoryData.backgroundImage = url + categoryData.backgroundImage; } res.locals.metaTags.push({ property: 'og:image', @@ -171,7 +174,7 @@ function addTags(categoryData, res) { res.locals.linkTags = [ { rel: 'up', - href: nconf.get('url'), + href: url, }, ]; diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 54b336aa18..3c1ace728f 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -14,6 +14,9 @@ const middleware = require('../middleware'); const helpers = module.exports; +const relative_path = nconf.get('relative_path'); +const url = nconf.get('url'); + helpers.noScriptErrors = async function (req, res, error, httpStatus) { if (req.body.noscript !== 'true') { return res.status(httpStatus).send(error); @@ -37,7 +40,7 @@ helpers.terms = { }; helpers.buildQueryString = function (query, key, value) { - const queryObj = _.clone(query); + const queryObj = { ...query }; if (value) { queryObj[key] = value; } else { @@ -51,11 +54,11 @@ helpers.addLinkTags = function (params) { params.res.locals.linkTags = params.res.locals.linkTags || []; params.res.locals.linkTags.push({ rel: 'canonical', - href: nconf.get('url') + '/' + params.url, + href: url + '/' + params.url, }); params.tags.forEach(function (rel) { - rel.href = nconf.get('url') + '/' + params.url + rel.href; + rel.href = url + '/' + params.url + rel.href; params.res.locals.linkTags.push(rel); }); }; @@ -136,7 +139,7 @@ helpers.notAllowed = async function (req, res, error) { helpers.formatApiResponse(401, res, error); } else { req.session.returnTo = req.url; - res.redirect(nconf.get('relative_path') + '/login'); + res.redirect(relative_path + '/login'); } }; @@ -144,7 +147,7 @@ helpers.redirect = function (res, url, permanent) { if (res.locals.isAPI) { res.set('X-Redirect', encodeURI(url)).status(200).json(url); } else { - res.redirect(permanent ? 308 : 307, nconf.get('relative_path') + encodeURI(url)); + res.redirect(permanent ? 308 : 307, relative_path + encodeURI(url)); } }; @@ -157,7 +160,7 @@ helpers.buildCategoryBreadcrumbs = async function (cid) { if (!data.disabled && !data.isSection) { breadcrumbs.unshift({ text: String(data.name), - url: nconf.get('relative_path') + '/category/' + data.slug, + url: relative_path + '/category/' + data.slug, cid: cid, }); } @@ -166,13 +169,13 @@ helpers.buildCategoryBreadcrumbs = async function (cid) { if (meta.config.homePageRoute && meta.config.homePageRoute !== 'categories') { breadcrumbs.unshift({ text: '[[global:header.categories]]', - url: nconf.get('relative_path') + '/categories', + url: relative_path + '/categories', }); } breadcrumbs.unshift({ text: '[[global:home]]', - url: nconf.get('relative_path') + '/', + url: relative_path + '/', }); return breadcrumbs; @@ -182,14 +185,14 @@ helpers.buildBreadcrumbs = function (crumbs) { const breadcrumbs = [ { text: '[[global:home]]', - url: nconf.get('relative_path') + '/', + url: relative_path + '/', }, ]; crumbs.forEach(function (crumb) { if (crumb) { if (crumb.url) { - crumb.url = nconf.get('relative_path') + crumb.url; + crumb.url = relative_path + crumb.url; } breadcrumbs.push(crumb); } diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 26c1b5715f..0387e4b69b 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -14,6 +14,10 @@ const analytics = require('../analytics'); const topicsController = module.exports; +const url = nconf.get('url'); +const relative_path = nconf.get('relative_path'); +const upload_url = nconf.get('upload_url'); + topicsController.get = async function getTopic(req, res, callback) { const tid = req.params.topic_id; @@ -79,7 +83,7 @@ topicsController.get = async function getTopic(req, res, callback) { topicData.scrollToMyPost = settings.scrollToMyPost; topicData.allowMultipleBadges = meta.config.allowMultipleBadges === 1; topicData.privateUploads = meta.config.privateUploads === 1; - topicData.rssFeedUrl = nconf.get('relative_path') + '/topic/' + topicData.tid + '.rss'; + topicData.rssFeedUrl = relative_path + '/topic/' + topicData.tid + '.rss'; if (req.loggedIn) { topicData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken; } @@ -96,7 +100,7 @@ topicsController.get = async function getTopic(req, res, callback) { topicData.pagination = pagination.create(currentPage, pageCount, req.query); topicData.pagination.rel.forEach(function (rel) { - rel.href = nconf.get('url') + '/topic/' + topicData.slug + rel.href; + rel.href = url + '/topic/' + topicData.slug + rel.href; res.locals.linkTags.push(rel); }); @@ -147,7 +151,7 @@ async function buildBreadcrumbs(topicData) { const breadcrumbs = [ { text: topicData.category.name, - url: nconf.get('relative_path') + '/category/' + topicData.category.slug, + url: relative_path + '/category/' + topicData.category.slug, cid: topicData.category.cid, }, { @@ -211,7 +215,7 @@ async function addTags(topicData, req, res) { res.locals.linkTags = [ { rel: 'canonical', - href: nconf.get('url') + '/topic/' + topicData.slug, + href: url + '/topic/' + topicData.slug, }, ]; @@ -226,7 +230,7 @@ async function addTags(topicData, req, res) { if (topicData.category) { res.locals.linkTags.push({ rel: 'up', - href: nconf.get('url') + '/category/' + topicData.category.slug, + href: url + '/category/' + topicData.category.slug, }); } } @@ -234,7 +238,7 @@ async function addTags(topicData, req, res) { async function addOGImageTags(res, topicData, postAtIndex) { const uploads = postAtIndex ? await posts.uploads.listWithSizes(postAtIndex.pid) : []; const images = uploads.map((upload) => { - upload.name = nconf.get('url') + nconf.get('upload_url') + '/files/' + upload.name; + upload.name = url + upload_url + '/files/' + upload.name; return upload; }); if (topicData.thumb) { @@ -252,7 +256,7 @@ async function addOGImageTags(res, topicData, postAtIndex) { function addOGImageTag(res, image) { let imageUrl; if (typeof image === 'string' && !image.startsWith('http')) { - imageUrl = nconf.get('url') + image.replace(new RegExp('^' + nconf.get('relative_path')), ''); + imageUrl = url + image.replace(new RegExp('^' + relative_path), ''); } else if (typeof image === 'object') { imageUrl = image.name; } else { @@ -327,7 +331,7 @@ topicsController.pagination = async function (req, res, callback) { const paginationData = pagination.create(currentPage, pageCount); paginationData.rel.forEach(function (rel) { - rel.href = nconf.get('url') + '/topic/' + topic.slug + rel.href; + rel.href = url + '/topic/' + topic.slug + rel.href; }); res.json({ pagination: paginationData }); diff --git a/src/coverPhoto.js b/src/coverPhoto.js index ddc4bc0578..7d18480e11 100644 --- a/src/coverPhoto.js +++ b/src/coverPhoto.js @@ -4,6 +4,8 @@ const nconf = require('nconf'); const meta = require('./meta'); +const relative_path = nconf.get('relative_path'); + const coverPhoto = module.exports; coverPhoto.getDefaultGroupCover = function (groupName) { @@ -15,7 +17,7 @@ coverPhoto.getDefaultProfileCover = function (uid) { }; function getCover(type, id) { - const defaultCover = nconf.get('relative_path') + '/assets/images/cover-default.png'; + const defaultCover = relative_path + '/assets/images/cover-default.png'; if (meta.config[type + ':defaultCovers']) { const covers = String(meta.config[type + ':defaultCovers']).trim().split(/[\s,]+/g); let coverPhoto = defaultCover; @@ -29,7 +31,7 @@ function getCover(type, id) { id %= covers.length; } if (covers[id]) { - coverPhoto = covers[id].startsWith('http') ? covers[id] : (nconf.get('relative_path') + covers[id]); + coverPhoto = covers[id].startsWith('http') ? covers[id] : (relative_path + covers[id]); } return coverPhoto; } diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 84cd1ef411..75d3efa06c 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -1,9 +1,8 @@ 'use strict'; module.exports = function (module) { - var helpers = require('./helpers'); + const helpers = require('./helpers'); - var _ = require('lodash'); const cache = require('../cache').create('mongo'); module.objectCache = cache; @@ -17,7 +16,7 @@ module.exports = function (module) { const writeData = helpers.serializeData(data); try { if (isArray) { - var bulk = module.client.collection('objects').initializeUnorderedBulkOp(); + const bulk = module.client.collection('objects').initializeUnorderedBulkOp(); key.forEach(key => bulk.find({ _key: key }).upsert().updateOne({ $set: writeData })); await bulk.execute(); } else { @@ -85,42 +84,33 @@ module.exports = function (module) { return []; } const cachedData = {}; - function returnData() { - var mapped = keys.map(function (key) { - if (!fields.length) { - return _.clone(cachedData[key]); - } - - const item = cachedData[key] || {}; - const result = {}; - fields.forEach((field) => { - result[field] = item[field] !== undefined ? item[field] : null; - }); - return result; - }); - - return mapped; - } - const unCachedKeys = cache.getUnCachedKeys(keys, cachedData); - if (!unCachedKeys.length) { - return returnData(); - } - - var query = { _key: { $in: unCachedKeys } }; - if (unCachedKeys.length === 1) { - query._key = unCachedKeys[0]; + let data = []; + if (unCachedKeys.length >= 1) { + data = await module.client.collection('objects').find( + { _key: unCachedKeys.length === 1 ? unCachedKeys[0] : { $in: unCachedKeys } }, + { projection: { _id: 0 } } + ).toArray(); + data = data.map(helpers.deserializeData); } - let data = await module.client.collection('objects').find(query, { projection: { _id: 0 } }).toArray(); - data = data.map(helpers.deserializeData); - var map = helpers.toMap(data); + const map = helpers.toMap(data); unCachedKeys.forEach(function (key) { cachedData[key] = map[key] || null; cache.set(key, cachedData[key]); }); - return returnData(); + if (!fields.length) { + return keys.map(key => (cachedData[key] ? { ...cachedData[key] } : null)); + } + return keys.map(function (key) { + const item = cachedData[key] || {}; + const result = {}; + fields.forEach((field) => { + result[field] = item[field] !== undefined ? item[field] : null; + }); + return result; + }); }; module.getObjectKeys = async function (key) { diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index f615c989c5..0426485892 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -1,9 +1,7 @@ 'use strict'; module.exports = function (module) { - var helpers = require('./helpers'); - - const _ = require('lodash'); + const helpers = require('./helpers'); const cache = require('../cache').create('redis'); @@ -110,11 +108,10 @@ module.exports = function (module) { cache.set(key, cachedData[key]); }); - const mapped = keys.map(function (key) { - if (!fields.length) { - return _.clone(cachedData[key]); - } - + if (!fields.length) { + return keys.map(key => (cachedData[key] ? { ...cachedData[key] } : null)); + } + return keys.map(function (key) { const item = cachedData[key] || {}; const result = {}; fields.forEach((field) => { @@ -122,7 +119,6 @@ module.exports = function (module) { }); return result; }); - return mapped; }; module.getObjectKeys = async function (key) { diff --git a/src/meta/errors.js b/src/meta/errors.js index 6aff66da17..763c736d5d 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -1,6 +1,5 @@ 'use strict'; -const _ = require('lodash'); const winston = require('winston'); const validator = require('validator'); const cronJob = require('cron').CronJob; @@ -18,7 +17,7 @@ new cronJob('0 * * * * *', function () { Errors.writeData = async function () { try { - const _counters = _.clone(counters); + const _counters = { ...counters }; counters = {}; const keys = Object.keys(_counters); if (!keys.length) { diff --git a/src/meta/tags.js b/src/meta/tags.js index 2fa073d4ad..c54c79554a 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -9,6 +9,10 @@ const utils = require('../utils'); const Tags = module.exports; +const url = nconf.get('url'); +const relative_path = nconf.get('relative_path'); +const upload_url = nconf.get('upload_url'); + Tags.parse = async (req, data, meta, link) => { // Meta tags const defaultTags = [{ @@ -29,7 +33,7 @@ Tags.parse = async (req, data, meta, link) => { content: Meta.config.title || 'NodeBB', }, { name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml', + content: 'frequency=30; polling-uri=' + url + '/sitemap.xml', noEscape: true, }, { name: 'theme-color', @@ -55,10 +59,10 @@ Tags.parse = async (req, data, meta, link) => { var defaultLinks = [{ rel: 'icon', type: 'image/x-icon', - href: nconf.get('relative_path') + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), + href: relative_path + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), }, { rel: 'manifest', - href: nconf.get('relative_path') + '/manifest.webmanifest', + href: relative_path + '/manifest.webmanifest', }]; if (plugins.hasListeners('filter:search.query')) { @@ -66,7 +70,7 @@ Tags.parse = async (req, data, meta, link) => { rel: 'search', type: 'application/opensearchdescription+xml', title: utils.escapeHTML(String(Meta.config.title || Meta.config.browserTitle || 'NodeBB')), - href: nconf.get('relative_path') + '/osd.xml', + href: relative_path + '/osd.xml', }); } @@ -74,64 +78,64 @@ Tags.parse = async (req, data, meta, link) => { if (Meta.config['brand:touchIcon']) { defaultLinks.push({ rel: 'apple-touch-icon', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-orig.png', + href: relative_path + upload_url + '/system/touchicon-orig.png', }, { rel: 'icon', sizes: '36x36', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-36.png', + href: relative_path + upload_url + '/system/touchicon-36.png', }, { rel: 'icon', sizes: '48x48', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-48.png', + href: relative_path + upload_url + '/system/touchicon-48.png', }, { rel: 'icon', sizes: '72x72', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-72.png', + href: relative_path + upload_url + '/system/touchicon-72.png', }, { rel: 'icon', sizes: '96x96', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-96.png', + href: relative_path + upload_url + '/system/touchicon-96.png', }, { rel: 'icon', sizes: '144x144', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-144.png', + href: relative_path + upload_url + '/system/touchicon-144.png', }, { rel: 'icon', sizes: '192x192', - href: nconf.get('relative_path') + nconf.get('upload_url') + '/system/touchicon-192.png', + href: relative_path + upload_url + '/system/touchicon-192.png', }); } else { defaultLinks.push({ rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/assets/images/touch/512.png', + href: relative_path + '/assets/images/touch/512.png', }, { rel: 'icon', sizes: '36x36', - href: nconf.get('relative_path') + '/assets/images/touch/192.png', + href: relative_path + '/assets/images/touch/192.png', }, { rel: 'icon', sizes: '48x48', - href: nconf.get('relative_path') + '/assets/images/touch/144.png', + href: relative_path + '/assets/images/touch/144.png', }, { rel: 'icon', sizes: '72x72', - href: nconf.get('relative_path') + '/assets/images/touch/96.png', + href: relative_path + '/assets/images/touch/96.png', }, { rel: 'icon', sizes: '96x96', - href: nconf.get('relative_path') + '/assets/images/touch/72.png', + href: relative_path + '/assets/images/touch/72.png', }, { rel: 'icon', sizes: '144x144', - href: nconf.get('relative_path') + '/assets/images/touch/48.png', + href: relative_path + '/assets/images/touch/48.png', }, { rel: 'icon', sizes: '192x192', - href: nconf.get('relative_path') + '/assets/images/touch/36.png', + href: relative_path + '/assets/images/touch/36.png', }, { rel: 'icon', sizes: '512x512', - href: nconf.get('relative_path') + '/assets/images/touch/512.png', + href: relative_path + '/assets/images/touch/512.png', }); } @@ -156,7 +160,7 @@ Tags.parse = async (req, data, meta, link) => { addSiteOGImage(meta); addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); - var ogUrl = nconf.get('url') + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : ''); + var ogUrl = url + (req.originalUrl !== '/' ? stripRelativePath(req.originalUrl) : ''); addIfNotExists(meta, 'property', 'og:url', ogUrl); addIfNotExists(meta, 'name', 'description', Meta.config.description); addIfNotExists(meta, 'property', 'og:description', Meta.config.description); @@ -187,8 +191,8 @@ function addIfNotExists(meta, keyName, tagName, value) { } function stripRelativePath(url) { - if (url.startsWith(nconf.get('relative_path'))) { - return url.slice(nconf.get('relative_path').length); + if (url.startsWith(relative_path)) { + return url.slice(relative_path.length); } return url; @@ -198,7 +202,7 @@ function addSiteOGImage(meta) { const key = Meta.config['og:image'] ? 'og:image' : 'brand:logo'; var ogImage = stripRelativePath(Meta.config[key] || ''); if (ogImage && !ogImage.startsWith('http')) { - ogImage = nconf.get('url') + ogImage; + ogImage = url + ogImage; } if (ogImage) { @@ -225,11 +229,11 @@ function addSiteOGImage(meta) { // Push fallback logo meta.push({ property: 'og:image', - content: nconf.get('url') + '/assets/images/logo@3x.png', + content: url + '/assets/images/logo@3x.png', noEscape: true, }, { property: 'og:image:url', - content: nconf.get('url') + '/assets/images/logo@3x.png', + content: url + '/assets/images/logo@3x.png', noEscape: true, }, { property: 'og:image:width', diff --git a/src/middleware/header.js b/src/middleware/header.js index df491fe6d1..b51d5404b4 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -26,6 +26,8 @@ var controllers = { const middleware = module.exports; +const relative_path = nconf.get('relative_path'); + middleware.buildHeader = helpers.try(async function buildHeader(req, res, next) { res.locals.renderHeader = true; res.locals.isAPI = false; @@ -54,7 +56,7 @@ async function generateHeader(req, res, data) { allowRegistration: registrationType === 'normal', searchEnabled: plugins.hasListeners('filter:search.query'), config: res.locals.config, - relative_path: nconf.get('relative_path'), + relative_path, bodyClass: data.bodyClass, }; diff --git a/src/middleware/headers.js b/src/middleware/headers.js index 27d5558e4d..93f90a1ef0 100644 --- a/src/middleware/headers.js +++ b/src/middleware/headers.js @@ -100,7 +100,6 @@ module.exports = function (middleware) { const defaultLang = meta.config.defaultLang || 'en-GB'; try { const codes = await languages.listCodes(); - winston.verbose('[middleware/autoLocale] Retrieves languages list for middleware'); return _.uniq([defaultLang, ...codes]); } catch (err) { winston.error('[middleware/autoLocale] Could not retrieve languages codes list! ' + err.stack); diff --git a/src/middleware/index.js b/src/middleware/index.js index 28f4794db1..1a9309cd1c 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -29,6 +29,8 @@ var delayCache = new LRU({ var middleware = module.exports; +const relative_path = nconf.get('relative_path'); + middleware.regexes = { timestampedUpload: /^\d+-.+$/, }; @@ -50,7 +52,7 @@ middleware.applyCSRF = function (req, res, next) { }; middleware.applyCSRFasync = util.promisify(middleware.applyCSRF); -middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login'); +middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(relative_path + '/login'); Object.assign(middleware, { admin: require('./admin'), @@ -64,24 +66,23 @@ require('./expose')(middleware); middleware.assert = require('./assert'); middleware.stripLeadingSlashes = function stripLeadingSlashes(req, res, next) { - var target = req.originalUrl.replace(nconf.get('relative_path'), ''); + var target = req.originalUrl.replace(relative_path, ''); if (target.startsWith('//')) { - return res.redirect(nconf.get('relative_path') + target.replace(/^\/+/, '/')); + return res.redirect(relative_path + target.replace(/^\/+/, '/')); } next(); }; middleware.pageView = helpers.try(async function pageView(req, res, next) { - const promises = [ - analytics.pageView({ ip: req.ip, uid: req.uid }), - ]; if (req.loggedIn) { - promises.push(user.updateOnlineUsers(req.uid)); - promises.push(user.updateLastOnlineTime(req.uid)); + await Promise.all([ + user.updateOnlineUsers(req.uid), + user.updateLastOnlineTime(req.uid), + ]); } - await Promise.all(promises); - plugins.fireHook('action:middleware.pageView', { req: req }); next(); + await analytics.pageView({ ip: req.ip, uid: req.uid }); + plugins.fireHook('action:middleware.pageView', { req: req }); }); middleware.pluginHooks = helpers.try(async function pluginHooks(req, res, next) { diff --git a/src/middleware/render.js b/src/middleware/render.js index 5edc087262..5a8a239054 100644 --- a/src/middleware/render.js +++ b/src/middleware/render.js @@ -1,6 +1,5 @@ 'use strict'; -const util = require('util'); const nconf = require('nconf'); const validator = require('validator'); const winston = require('winston'); @@ -11,6 +10,9 @@ const translator = require('../translator'); const widgets = require('../widgets'); const utils = require('../utils'); const slugify = require('../slugify'); +const cache = require('../cache'); + +const relative_path = nconf.get('relative_path'); module.exports = function (middleware) { middleware.processRender = function processRender(req, res, next) { @@ -27,7 +29,7 @@ module.exports = function (middleware) { } options.loggedIn = req.uid > 0; - options.relative_path = nconf.get('relative_path'); + options.relative_path = relative_path; options.template = { name: template, [template]: true }; options.url = (req.baseUrl + req.path.replace(/^\/api/, '')); options.bodyClass = buildBodyClass(req, res, options); @@ -57,52 +59,75 @@ module.exports = function (middleware) { req.app.set('json spaces', global.env === 'development' || req.query.pretty ? 4 : 0); return res.json(options); } - const ajaxifyData = JSON.stringify(options).replace(/<\//g, '<\\/'); - - const renderAsync = util.promisify((templateToRender, options, next) => render.call(self, templateToRender, options, next)); const results = await utils.promiseParallel({ header: renderHeaderFooter('renderHeader', req, res, options), - content: renderAsync(templateToRender, options), + content: renderContent(render, templateToRender, req, res, options), footer: renderHeaderFooter('renderFooter', req, res, options), }); const str = results.header + (res.locals.postHeader || '') + - results.content + '' + + results.content + + '' + (res.locals.preFooter || '') + results.footer; - let translated = await translate(str, req, res); - translated = translated.replace('', function () { - return ''; - }); - if (typeof fn !== 'function') { - self.send(translated); + self.send(str); } else { - fn(null, translated); + fn(null, str); } }; next(); }; + async function renderContent(render, tpl, req, res, options) { + return new Promise(function (resolve, reject) { + render.call(res, tpl, options, async function (err, str) { + if (err) reject(err); + else resolve(await translate(str, getLang(req, res))); + }); + }); + } + async function renderHeaderFooter(method, req, res, options) { - if (res.locals.renderHeader) { - return await middleware[method](req, res, options); - } else if (res.locals.renderAdminHeader) { - return await middleware.admin[method](req, res, options); + let str = ''; + const lang = getLang(req, res); + if (req.uid === 0 && res.locals.renderHeader) { + str = cache.get('render' + options.template.name + lang + method); + if (str) { + return str; + } + } + if (!str) { + if (res.locals.renderHeader) { + str = await middleware[method](req, res, options); + } else if (res.locals.renderAdminHeader) { + str = await middleware.admin[method](req, res, options); + } else { + str = ''; + } } - return ''; + const translated = await translate(str, lang); + if (req.uid === 0 && res.locals.renderHeader) { + cache.set('render' + options.template.name + lang + method, translated, 300000); + } + return translated; } - async function translate(str, req, res) { + function getLang(req, res) { let language = (res.locals.config && res.locals.config.userLang) || 'en-GB'; if (res.locals.renderAdminHeader) { language = (res.locals.config && res.locals.config.acpLang) || 'en-GB'; } - language = req.query.lang ? validator.escape(String(req.query.lang)) : language; + return req.query.lang ? validator.escape(String(req.query.lang)) : language; + } + + async function translate(str, language) { const translated = await translator.translate(str, language); return translator.unescape(translated); } diff --git a/src/navigation/admin.js b/src/navigation/admin.js index b139660755..6cfba3b738 100644 --- a/src/navigation/admin.js +++ b/src/navigation/admin.js @@ -1,6 +1,5 @@ 'use strict'; -const _ = require('lodash'); const validator = require('validator'); const plugins = require('../plugins'); @@ -54,7 +53,7 @@ function toggleEscape(navItems, flag) { admin.get = async function () { if (cache) { - return _.cloneDeep(cache); + return cache.map(item => ({ ...item })); } const data = await db.getSortedSetRange('navigation:enabled', 0, -1); cache = data.map(function (item) { @@ -67,7 +66,7 @@ admin.get = async function () { }); admin.escapeFields(cache); - return _.cloneDeep(cache); + return cache.map(item => ({ ...item })); }; async function getAvailable() { diff --git a/src/navigation/index.js b/src/navigation/index.js index f679cc29a3..ed639afbfa 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -6,6 +6,8 @@ const groups = require('../groups'); const navigation = module.exports; +const relative_path = nconf.get('relative_path'); + navigation.get = async function (uid) { let data = await admin.get(); @@ -13,7 +15,7 @@ navigation.get = async function (uid) { item.originalRoute = item.route; if (!item.route.startsWith('http')) { - item.route = nconf.get('relative_path') + item.route; + item.route = relative_path + item.route; } return item; diff --git a/src/pagination.js b/src/pagination.js index 52d3de7511..438ee58b70 100644 --- a/src/pagination.js +++ b/src/pagination.js @@ -38,7 +38,7 @@ pagination.create = function (currentPage, pageCount, queryObj) { return a - b; }); - queryObj = _.clone(queryObj || {}); + queryObj = { ...(queryObj || {}) }; delete queryObj._; diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 48d2f53b9d..529cc8059d 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -87,7 +87,7 @@ module.exports = function (Plugins) { Plugins.fireHook = async function (hook, params) { const hookList = Plugins.loadedHooks[hook]; const hookType = hook.split(':')[0]; - if (hook !== 'action:plugins.firehook') { + if (global.env === 'development' && hook !== 'action:plugins.firehook') { winston.verbose('[plugins/fireHook] ' + hook); } diff --git a/src/posts/uploads.js b/src/posts/uploads.js index 354692d1de..e44cae72e6 100644 --- a/src/posts/uploads.js +++ b/src/posts/uploads.js @@ -44,7 +44,7 @@ module.exports = function (Posts) { }; Posts.uploads.list = async function (pid) { - return await db.getSortedSetRange('post:' + pid + ':uploads', 0, -1); + return await db.getSortedSetMembers('post:' + pid + ':uploads'); }; Posts.uploads.listWithSizes = async function (pid) { diff --git a/src/prestart.js b/src/prestart.js index 6d9839a962..ed61e28c16 100644 --- a/src/prestart.js +++ b/src/prestart.js @@ -95,6 +95,11 @@ function loadConfig(configFile) { nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); nconf.set('port', nconf.get('PORT') || nconf.get('port') || urlObject.port || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + + // cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353 + const domain = nconf.get('cookieDomain') || urlObject.hostname; + const origins = nconf.get('socket.io:origins') || `${urlObject.protocol}//${domain}:*`; + nconf.set('socket.io:origins', origins); } } diff --git a/src/routes/helpers.js b/src/routes/helpers.js index 392cdaccac..cfcb9e941b 100644 --- a/src/routes/helpers.js +++ b/src/routes/helpers.js @@ -4,7 +4,12 @@ const helpers = module.exports; const controllerHelpers = require('../controllers/helpers'); helpers.setupPageRoute = function (router, name, middleware, middlewares, controller) { - middlewares = [middleware.maintenanceMode, middleware.registrationComplete, middleware.pageView, middleware.pluginHooks].concat(middlewares); + middlewares = [ + middleware.maintenanceMode, + middleware.registrationComplete, + middleware.pageView, + middleware.pluginHooks, + ].concat(middlewares); router.get(name, middleware.busyCheck, middleware.applyCSRF, middleware.buildHeader, middlewares, helpers.tryRoute(controller)); router.get('/api' + name, middlewares, helpers.tryRoute(controller)); diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 8e5261fd29..f4e802c711 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -3,7 +3,6 @@ const os = require('os'); const nconf = require('nconf'); const winston = require('winston'); -const url = require('url'); const util = require('util'); const cookieParser = require('cookie-parser')(nconf.get('secret')); @@ -47,14 +46,7 @@ Sockets.init = function (server) { * Can be overridden via config (socket.io:origins) */ if (process.env.NODE_ENV !== 'development') { - const parsedUrl = url.parse(nconf.get('url')); - - // cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353 - const domain = nconf.get('cookieDomain') || parsedUrl.hostname; - - const origins = nconf.get('socket.io:origins') || `${parsedUrl.protocol}//${domain}:*`; - nconf.set('socket.io:origins', origins); - + const origins = nconf.get('socket.io:origins'); io.origins(origins); winston.info('[socket.io] Restricting access to origin: ' + origins); } diff --git a/src/topics/index.js b/src/topics/index.js index 418797a3f7..e770d8d141 100644 --- a/src/topics/index.js +++ b/src/topics/index.js @@ -62,7 +62,14 @@ Topics.getTopicsByTids = async function (tids, options) { uid = options.uid; } - let topics = await Topics.getTopicsData(tids); + const [tags, topics, hasRead, isIgnored, bookmarks, callerSettings] = await Promise.all([ + Topics.getTopicsTagsObjects(tids), + Topics.getTopicsData(tids), + Topics.hasReadTopics(tids, uid), + Topics.isIgnoring(tids, uid), + Topics.getUserBookmarks(tids, uid), + user.getSettings(uid), + ]); const uids = _.uniq(topics.map(t => t && t.uid && t.uid.toString()).filter(v => utils.isNumber(v))); const cids = _.uniq(topics.map(t => t && t.cid && t.cid.toString()).filter(v => utils.isNumber(v))); @@ -72,26 +79,16 @@ Topics.getTopicsByTids = async function (tids, options) { return await Promise.all(guestTopics.map(topic => posts.getPostField(topic.mainPid, 'handle'))); } const [ - callerSettings, users, userSettings, categoriesData, - hasRead, - isIgnored, - bookmarks, teasers, - tags, guestHandles, ] = await Promise.all([ - user.getSettings(uid), user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status']), user.getMultipleUserSettings(uids), categories.getCategoriesFields(cids, ['cid', 'name', 'slug', 'icon', 'backgroundImage', 'imageClass', 'bgColor', 'color', 'disabled']), - Topics.hasReadTopics(tids, uid), - Topics.isIgnoring(tids, uid), - Topics.getUserBookmarks(tids, uid), Topics.getTeasers(topics, options), - Topics.getTopicsTagsObjects(tids), loadGuestHandles(), ]); @@ -128,9 +125,9 @@ Topics.getTopicsByTids = async function (tids, options) { } }); - topics = topics.filter(topic => topic && topic.category && !topic.category.disabled); + const filteredTopics = topics.filter(topic => topic && topic.category && !topic.category.disabled); - const result = await plugins.fireHook('filter:topics.get', { topics: topics, uid: uid }); + const result = await plugins.fireHook('filter:topics.get', { topics: filteredTopics, uid: uid }); return result.topics; }; diff --git a/src/topics/posts.js b/src/topics/posts.js index 883d66907f..a7a9189233 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -56,7 +56,7 @@ module.exports = function (Topics) { postData.forEach(function (postObj, i) { if (postObj) { - postObj.user = postObj.uid ? userData[postObj.uid] : _.clone(userData[postObj.uid]); + postObj.user = postObj.uid ? userData[postObj.uid] : { ...userData[postObj.uid] }; postObj.editor = postObj.editor ? editors[postObj.editor] : null; postObj.bookmarked = bookmarks[i]; postObj.upvoted = voteData.upvotes[i]; diff --git a/src/user/auth.js b/src/user/auth.js index 10a943b088..0a38609215 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -31,7 +31,7 @@ module.exports = function (User) { await db.delete('loginAttempts:' + uid); await db.pexpire('lockout:' + uid, duration); - events.log({ + await events.log({ type: 'account-locked', uid: uid, ip: ip, diff --git a/src/user/data.js b/src/user/data.js index 1f9a8e9dd7..c1fb6be11f 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -9,6 +9,8 @@ const meta = require('../meta'); const plugins = require('../plugins'); const utils = require('../utils'); +const relative_path = nconf.get('relative_path'); + const intFields = [ 'uid', 'postcount', 'topiccount', 'reputation', 'profileviews', 'banned', 'banned:expire', 'email:confirmed', 'joindate', 'lastonline', @@ -104,7 +106,7 @@ module.exports = function (User) { function uidsToUsers(uids, uniqueUids, usersData) { const uidToUser = _.zipObject(uniqueUids, usersData); const users = uids.map(function (uid) { - const returnPayload = uidToUser[uid] || _.clone(User.guestData); + const returnPayload = uidToUser[uid] || { ...User.guestData }; if (uid > 0 && !returnPayload.uid) { returnPayload.oldUid = parseInt(uid, 10); } @@ -164,10 +166,10 @@ module.exports = function (User) { } if (user.picture && user.picture === user.uploadedpicture) { - user.uploadedpicture = user.picture.startsWith('http') ? user.picture : nconf.get('relative_path') + user.picture; + user.uploadedpicture = user.picture.startsWith('http') ? user.picture : relative_path + user.picture; user.picture = user.uploadedpicture; } else if (user.uploadedpicture) { - user.uploadedpicture = user.uploadedpicture.startsWith('http') ? user.uploadedpicture : nconf.get('relative_path') + user.uploadedpicture; + user.uploadedpicture = user.uploadedpicture.startsWith('http') ? user.uploadedpicture : relative_path + user.uploadedpicture; } if (meta.config.defaultAvatar && !user.picture) { user.picture = User.getDefaultAvatar(); @@ -240,7 +242,7 @@ module.exports = function (User) { if (!meta.config.defaultAvatar) { return ''; } - return meta.config.defaultAvatar.startsWith('http') ? meta.config.defaultAvatar : nconf.get('relative_path') + meta.config.defaultAvatar; + return meta.config.defaultAvatar.startsWith('http') ? meta.config.defaultAvatar : relative_path + meta.config.defaultAvatar; }; User.setUserField = async function (uid, field, value) { diff --git a/src/webserver.js b/src/webserver.js index 26db9d1a35..bf347b1d38 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -50,7 +50,7 @@ module.exports.app = app; server.on('error', function (err) { if (err.code === 'EADDRINUSE') { - winston.error('NodeBB address in use, exiting...', err.stack); + winston.error('NodeBB address in use, exiting...\n' + err.stack); } else { winston.error(err.stack); } diff --git a/src/widgets/index.js b/src/widgets/index.js index 7a0754ae59..cbbd45c54b 100644 --- a/src/widgets/index.js +++ b/src/widgets/index.js @@ -3,14 +3,12 @@ const winston = require('winston'); const _ = require('lodash'); const Benchpress = require('benchpressjs'); -const util = require('util'); const plugins = require('../plugins'); const groups = require('../groups'); const translator = require('../translator'); const db = require('../database'); const apiController = require('../controllers/api'); -const loadConfigAsync = util.promisify(apiController.loadConfig); const meta = require('../meta'); const widgets = module.exports; @@ -59,7 +57,7 @@ async function renderWidget(widget, uid, options) { let config = options.res.locals.config || {}; if (options.res.locals.isAPI) { - config = await loadConfigAsync(options.req); + config = await apiController.loadConfig(options.req); } const userLang = config.userLang || meta.config.defaultLang || 'en-GB'; diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index f6788b3eb2..8cf0a07803 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -38,6 +38,13 @@ nconf.defaults({ const urlObject = url.parse(nconf.get('url')); const relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; nconf.set('relative_path', relativePath); +nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); +nconf.set('upload_url', '/assets/uploads'); + +// cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353 +const domain = nconf.get('cookieDomain') || urlObject.hostname; +const origins = nconf.get('socket.io:origins') || `${urlObject.protocol}//${domain}:*`; +nconf.set('socket.io:origins', origins); if (nconf.get('isCluster') === undefined) { nconf.set('isPrimary', true); @@ -125,10 +132,7 @@ before(async function () { nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); - nconf.set('relative_path', relativePath); nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); - nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); - nconf.set('upload_url', '/assets/uploads'); nconf.set('core_templates_path', path.join(__dirname, '../../src/views')); nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); diff --git a/test/user.js b/test/user.js index cec9238300..c17a0a288a 100644 --- a/test/user.js +++ b/test/user.js @@ -1161,10 +1161,8 @@ describe('User', function () { meta.config.defaultAvatar = 'https://path/to/default/avatar'; assert.strictEqual(User.getDefaultAvatar(), meta.config.defaultAvatar); meta.config.defaultAvatar = '/path/to/default/avatar'; - nconf.set('relative_path', '/community'); - assert.strictEqual(User.getDefaultAvatar(), '/community' + meta.config.defaultAvatar); + assert.strictEqual(User.getDefaultAvatar(), nconf.get('relative_path') + meta.config.defaultAvatar); meta.config.defaultAvatar = ''; - nconf.set('relative_path', ''); done(); });