From 05b68391ddacb92719fd19b1013d1d813ce50a40 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 14 Jan 2017 22:36:10 -0700
Subject: [PATCH 01/10] Use `/assets` for client assets
- Route `/assets` -> `build/public`, falling back on `public`
- Moved destinations for `nodebb.min.js`, `acp.min.js`, `admin.css`, `stylesheet.css`, `templates`, and `sounds` to `build/public`
- r.js modules previously routed through express are now symlinked into `public/build/src/modules`
- minfiles no longer served from a memory cache
- use config `views_dir` setting everywhere template directory is used
- fix cache buster `v=v=`
---
.eslintignore | 1 +
app.js | 2 +-
build.js | 1 +
public/src/ajaxify.js | 2 +-
public/src/app.js | 4 +-
public/src/client/account/header.js | 2 +-
public/src/modules/translator.js | 4 +-
public/src/require-config.js | 2 +-
public/src/widgets.js | 2 +-
src/admin/search.js | 5 +-
src/controllers/admin/settings.js | 3 +-
src/controllers/admin/uploads.js | 2 +-
src/coverPhoto.js | 2 +-
src/meta/css.js | 24 +-------
src/meta/js.js | 86 +++++++++-------------------
src/meta/sounds.js | 4 +-
src/meta/themes.js | 2 +-
src/middleware/admin.js | 2 +-
src/middleware/header.js | 2 +-
src/routes/index.js | 38 ++++++++++--
src/routes/meta.js | 30 ----------
src/views/admin/header.tpl | 26 ++++-----
src/views/admin/settings/group.tpl | 2 +-
src/views/admin/settings/uploads.tpl | 2 +-
src/webserver.js | 4 --
src/widgets/admin.js | 3 +-
test/mocks/databasemock.js | 2 +-
27 files changed, 101 insertions(+), 158 deletions(-)
diff --git a/.eslintignore b/.eslintignore
index 0799652254..250640f954 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -18,3 +18,4 @@ logs/
*.ipr
*.iws
/coverage
+/build
diff --git a/app.js b/app.js
index b6d7d07829..55cc0dbecf 100644
--- a/app.js
+++ b/app.js
@@ -99,7 +99,7 @@ function loadConfig(callback) {
nconf.defaults({
base_dir: __dirname,
themes_path: path.join(__dirname, 'node_modules'),
- views_dir: path.join(__dirname, 'public/templates'),
+ views_dir: path.join(__dirname, 'build/public/templates'),
version: pkg.version
});
diff --git a/build.js b/build.js
index a0250dfb37..32359e14f8 100644
--- a/build.js
+++ b/build.js
@@ -57,6 +57,7 @@ exports.buildTargets = function (targets, callback) {
winston.info('[build] Building javascript');
var startTime = Date.now();
async.series([
+ meta.js.linkModules,
async.apply(meta.js.minify, 'nodebb.min.js'),
async.apply(meta.js.minify, 'acp.min.js')
], step.bind(this, startTime, 'js', next));
diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js
index 2e895f380b..7eb46343e5 100644
--- a/public/src/ajaxify.js
+++ b/public/src/ajaxify.js
@@ -338,7 +338,7 @@ $(document).ready(function () {
callback(templates.cache[template]);
} else {
$.ajax({
- url: RELATIVE_PATH + '/templates/' + template + '.tpl' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''),
+ url: config.relative_path + '/assets/templates/' + template + '.tpl' + '?' + config['cache-buster'],
type: 'GET',
success: function (data) {
callback(data.toString());
diff --git a/public/src/app.js b/public/src/app.js
index fe12a7adfc..00d5e7c017 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -558,7 +558,7 @@ app.cacheBuster = null;
var scriptEl = document.createElement('script');
scriptEl.type = 'text/javascript';
- scriptEl.src = config.relative_path + '/vendor/jquery/js/jquery-ui.js' + (app.cacheBuster ? '?v=' + app.cacheBuster : '');
+ scriptEl.src = config.relative_path + '/vendor/jquery/js/jquery-ui.js' + '?' + config['cache-buster'];
scriptEl.onload = callback;
document.head.appendChild(scriptEl);
};
@@ -625,7 +625,7 @@ app.cacheBuster = null;
app.loadProgressiveStylesheet = function () {
var linkEl = document.createElement('link');
linkEl.rel = 'stylesheet';
- linkEl.href = config.relative_path + '/js-enabled.css';
+ linkEl.href = config.relative_path + '/assets/js-enabled.css';
document.head.appendChild(linkEl);
};
diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js
index e99b7ee0a8..d0f84ade3a 100644
--- a/public/src/client/account/header.js
+++ b/public/src/client/account/header.js
@@ -84,7 +84,7 @@ define('forum/account/header', [
params: {uid: ajaxify.data.uid },
accept: '.png,.jpg,.bmp'
}, function (imageUrlOnServer) {
- components.get('account/cover').css('background-image', 'url(' + imageUrlOnServer + '?v=' + Date.now() + ')');
+ components.get('account/cover').css('background-image', 'url(' + imageUrlOnServer + '?' + config['cache-buster'] + ')');
});
},
removeCover
diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js
index eac467f413..ff2aa53bb0 100644
--- a/public/src/modules/translator.js
+++ b/public/src/modules/translator.js
@@ -513,12 +513,12 @@
break;
}
- jQuery.getScript(config.relative_path + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').done(function () {
+ jQuery.getScript(config.relative_path + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').done(function () {
jQuery('.timeago').timeago();
adaptor.timeagoShort = assign({}, jQuery.timeago.settings.strings);
// Retrieve the shorthand timeago values as well
- jQuery.getScript(config.relative_path + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').done(function () {
+ jQuery.getScript(config.relative_path + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').done(function () {
// Switch back to long-form
adaptor.toggleTimeagoShorthand();
});
diff --git a/public/src/require-config.js b/public/src/require-config.js
index 8618685052..0e021f6be0 100644
--- a/public/src/require-config.js
+++ b/public/src/require-config.js
@@ -1,5 +1,5 @@
require.config({
- baseUrl: config.relative_path + "/src/modules",
+ baseUrl: config.relative_path + '/assets/src/modules',
waitSeconds: 7,
urlArgs: config['cache-buster'],
paths: {
diff --git a/public/src/widgets.js b/public/src/widgets.js
index 46023fad15..377ccb2c53 100644
--- a/public/src/widgets.js
+++ b/public/src/widgets.js
@@ -29,7 +29,7 @@
}
});
- $.get(config.relative_path + '/api/widgets/render' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), {
+ $.get(config.relative_path + '/api/widgets/render' + '?' + config['cache-buster'], {
locations: widgetLocations,
template: template + '.tpl',
url: url,
diff --git a/src/admin/search.js b/src/admin/search.js
index 860c527e6b..4c53815e48 100644
--- a/src/admin/search.js
+++ b/src/admin/search.js
@@ -4,6 +4,7 @@ var fs = require('fs');
var path = require('path');
var async = require('async');
var sanitizeHTML = require('sanitize-html');
+var nconf = require('nconf');
var utils = require('../../public/src/utils');
var Translator = require('../../public/src/modules/translator').Translator;
@@ -23,7 +24,7 @@ function filterDirectories(directories) {
}
function getAdminNamespaces(callback) {
- utils.walk(path.resolve(__dirname, '../../public/templates/admin'), function (err, directories) {
+ utils.walk(path.resolve(nconf.get('views_dir'), 'admin'), function (err, directories) {
if (err) {
return callback(err);
}
@@ -60,7 +61,7 @@ var fallbackCacheInProgress = {};
var fallbackCache = {};
function initFallback(namespace, callback) {
- fs.readFile(path.resolve(__dirname, '../../public/templates/', namespace + '.tpl'), function (err, file) {
+ fs.readFile(path.resolve(nconf.get('views_dir'), namespace + '.tpl'), function (err, file) {
if (err) {
return callback(err);
}
diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js
index 639267f5f5..0d1f509631 100644
--- a/src/controllers/admin/settings.js
+++ b/src/controllers/admin/settings.js
@@ -2,6 +2,7 @@
var async = require('async');
+var nconf = require('nconf');
var meta = require('../../meta');
var settingsController = module.exports;
@@ -25,7 +26,7 @@ function renderEmail(req, res, next) {
var path = require('path');
var utils = require('../../../public/src/utils');
- var emailsPath = path.join(__dirname, '../../../public/templates/emails');
+ var emailsPath = path.join(nconf.get('views_dir'), 'emails');
async.waterfall([
function (next) {
diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js
index d1664cb8f1..2b1a60e281 100644
--- a/src/controllers/admin/uploads.js
+++ b/src/controllers/admin/uploads.js
@@ -105,7 +105,7 @@ uploadsController.uploadSound = function (req, res, next) {
return next(err);
}
- var soundsPath = path.join(__dirname, '../../../public/sounds'),
+ var soundsPath = path.join(__dirname, '../../../build/public/sounds'),
filePath = path.join(__dirname, '../../../public/uploads/sounds', uploadedFile.name);
if (process.platform === 'win32') {
diff --git a/src/coverPhoto.js b/src/coverPhoto.js
index 699e4ee374..9380b347f4 100644
--- a/src/coverPhoto.js
+++ b/src/coverPhoto.js
@@ -26,7 +26,7 @@ function getCover(type, id) {
return covers[id];
}
- return nconf.get('relative_path') + '/images/cover-default.png';
+ return nconf.get('relative_path') + '/assets/images/cover-default.png';
}
module.exports = coverPhoto;
\ No newline at end of file
diff --git a/src/meta/css.js b/src/meta/css.js
index 0d63e09d6d..f72ef6004d 100644
--- a/src/meta/css.js
+++ b/src/meta/css.js
@@ -86,15 +86,6 @@ module.exports = function (Meta) {
});
};
- Meta.css.getFromFile = function (callback) {
- async.series([
- async.apply(Meta.css.loadFile, path.join(__dirname, '../../public/stylesheet.css'), 'cache'),
- async.apply(Meta.css.loadFile, path.join(__dirname, '../../public/admin.css'), 'acpCache')
- ], function (err) {
- callback(err);
- });
- };
-
function getStyleSource(files, prefix, extension, callback) {
var pluginDirectories = [],
source = '';
@@ -127,7 +118,7 @@ module.exports = function (Meta) {
Meta.css.commitToFile = function (filename, callback) {
var file = (filename === 'acpCache' ? 'admin' : 'stylesheet') + '.css';
- fs.writeFile(path.join(__dirname, '../../public/' + file), Meta.css[filename], function (err) {
+ fs.writeFile(path.join(__dirname, '../../build/public/' + file), Meta.css[filename], function (err) {
if (!err) {
winston.verbose('[meta/css] ' + file + ' committed to disk.');
} else {
@@ -139,19 +130,6 @@ module.exports = function (Meta) {
});
};
- Meta.css.loadFile = function (filePath, filename, callback) {
- winston.verbose('[meta/css] Reading stylesheet ' + filePath.split('/').pop() + ' from file');
-
- fs.readFile(filePath, function (err, file) {
- if (err) {
- return callback(err);
- }
-
- Meta.css[filename] = file;
- callback();
- });
- };
-
function minify(source, paths, destination, callback) {
callback = callback || function () {};
less.render(source, {
diff --git a/src/meta/js.js b/src/meta/js.js
index 7f39235993..8c3794043f 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -6,6 +6,7 @@ var path = require('path');
var async = require('async');
var nconf = require('nconf');
var fs = require('fs');
+var mkdirp = require('mkdirp');
var plugins = require('../plugins');
var utils = require('../../public/src/utils');
@@ -85,30 +86,34 @@ module.exports = function (Meta) {
}
}
};
-
- Meta.js.bridgeModules = function (app, callback) {
- // Add routes for AMD-type modules to serve those files
- function addRoute(relPath) {
- var relativePath = nconf.get('relative_path');
-
- app.get(relativePath + '/src/modules/' + relPath, function (req, res) {
- return res.sendFile(path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]), {
- maxAge: app.enabled('cache') ? 5184000000 : 0
- });
- });
- }
-
- var numBridged = 0;
-
- for(var relPath in Meta.js.scripts.modules) {
- if (Meta.js.scripts.modules.hasOwnProperty(relPath)) {
- addRoute(relPath);
- ++numBridged;
+
+ Meta.js.linkModules = function (callback) {
+ function link(filePath, destPath, cb) {
+ if (process.platform === 'win32') {
+ fs.link(filePath, destPath, cb);
+ } else {
+ fs.symlink(filePath, destPath, 'file', cb);
}
}
- winston.verbose('[meta/js] ' + numBridged + ' of ' + Object.keys(Meta.js.scripts.modules).length + ' modules bridged');
- callback();
+ plugins.reload(function (err) {
+ if (err) {
+ return callback(err);
+ }
+
+ async.each(Object.keys(Meta.js.scripts.modules), function (relPath, next) {
+ var filePath = path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]);
+ var destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
+
+ mkdirp(path.dirname(destPath), function (err) {
+ if (err) {
+ return next(err);
+ }
+
+ link(filePath, destPath, next);
+ });
+ }, callback);
+ });
};
Meta.js.minify = function (target, callback) {
@@ -202,48 +207,11 @@ module.exports = function (Meta) {
};
Meta.js.commitToFile = function (target, callback) {
- fs.writeFile(path.join(__dirname, '../../public/' + target), Meta.js.target[target].cache, function (err) {
+ fs.writeFile(path.join(__dirname, '../../build/public/' + target), Meta.js.target[target].cache, function (err) {
callback(err);
});
};
- Meta.js.getFromFile = function (target, callback) {
- function readFile(filePath, next) {
- fs.readFile(filePath, function (err, contents) {
- if (err) {
- if (err.code === 'ENOENT') {
- if (!filePath.endsWith('.map')) {
- winston.warn('[meta/js] ' + filePath + ' not found on disk, did you run ./nodebb build?');
- }
- return next(null, '');
- }
- }
- next(err, contents);
- });
- }
-
- var scriptPath = path.join(nconf.get('base_dir'), 'public/' + target);
- var mapPath = path.join(nconf.get('base_dir'), 'public/' + target + '.map');
-
- async.parallel({
- script: function (next) {
- readFile(scriptPath, next);
- },
- map: function (next) {
- readFile(mapPath, next);
- }
- }, function (err, results) {
- if (err) {
- return callback(err);
- }
- Meta.js.target[target] = {
- cache: results.script,
- map: results.map
- };
- callback();
- });
- };
-
function setupDebugging() {
/**
* Check if the parent process is running with the debug option --debug (or --debug-brk)
diff --git a/src/meta/sounds.js b/src/meta/sounds.js
index 6068f16f5f..6acbd77a1f 100644
--- a/src/meta/sounds.js
+++ b/src/meta/sounds.js
@@ -28,7 +28,7 @@ module.exports = function (Meta) {
Meta.sounds.getFiles = function (callback) {
async.waterfall([
function (next) {
- fs.readdir(path.join(__dirname, '../../public/sounds'), next);
+ fs.readdir(path.join(__dirname, '../../build/public/sounds'), next);
},
function (sounds, next) {
fs.readdir(path.join(__dirname, '../../public/uploads/sounds'), function (err, uploaded) {
@@ -88,7 +88,7 @@ module.exports = function (Meta) {
};
function setupSounds(callback) {
- var soundsPath = path.join(__dirname, '../../public/sounds');
+ var soundsPath = path.join(__dirname, '../../build/public/sounds');
async.waterfall([
function (next) {
diff --git a/src/meta/themes.js b/src/meta/themes.js
index be48d1e70d..e19dcdbf4c 100644
--- a/src/meta/themes.js
+++ b/src/meta/themes.js
@@ -48,7 +48,7 @@ module.exports = function (Meta) {
if (configObj.screenshot) {
configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id;
} else {
- configObj.screenshot_url = nconf.get('relative_path') + '/images/themes/default.png';
+ configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png';
}
next(null, configObj);
} catch (err) {
diff --git a/src/middleware/admin.js b/src/middleware/admin.js
index 3240eaf0af..9d4b43bf25 100644
--- a/src/middleware/admin.js
+++ b/src/middleware/admin.js
@@ -101,7 +101,7 @@ module.exports = function (middleware) {
plugins: results.custom_header.plugins,
authentication: results.custom_header.authentication,
scripts: results.scripts,
- 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
+ 'cache-buster': meta.config['cache-buster'] || '',
env: process.env.NODE_ENV ? true : false,
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
bodyClass: data.bodyClass
diff --git a/src/middleware/header.js b/src/middleware/header.js
index c1b4175b5b..5e78416a0a 100644
--- a/src/middleware/header.js
+++ b/src/middleware/header.js
@@ -46,7 +46,7 @@ module.exports = function (middleware) {
bootswatchCSS: meta.config['theme:src'],
title: meta.config.title || '',
description: meta.config.description || '',
- 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '',
+ 'cache-buster': meta.config['cache-buster'] || '',
'brand:logo': meta.config['brand:logo'] || '',
'brand:logo:url': meta.config['brand:logo:url'] || '',
'brand:logo:alt': meta.config['brand:logo:alt'] || '',
diff --git a/src/routes/index.js b/src/routes/index.js
index 158774b624..e1f95891cf 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -145,21 +145,47 @@ module.exports = function (app, middleware, hotswapIds) {
}
app.use(middleware.privateUploads);
- app.use(relativePath + '/assets', express.static(path.join(__dirname, '../../', 'build/public'), {
+
+ app.use(relativePath + '/assets', express.static(path.join(__dirname, '../../build/public'), {
+ maxAge: app.enabled('cache') ? 5184000000 : 0
+ }));
+ app.use(relativePath + '/assets', express.static(path.join(__dirname, '../../public'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
+ // DEPRECATED
+ var deprecatedPaths = [
+ '/nodebb.min.js',
+ '/acp.min.js',
+ '/stylesheet.css',
+ '/js-enabled.css',
+ '/admin.css',
+ '/logo.png',
+ '/favicon.ico',
+ '/vendor/',
+ '/uploads/',
+ '/templates/',
+ '/src/',
+ '/images/',
+ // '/sounds/',
+ ];
+ app.use(relativePath, function (req, res, next) {
+ if (deprecatedPaths.some(function (path) { return req.path.startsWith(path); })) {
+ winston.warn('[deprecated] Accessing `' + req.path.slice(1) + '` from `/` is deprecated. ' +
+ 'Use `/assets' + req.path + '` to access this file.');
+ res.redirect(relativePath + '/assets' + req.path + '?' + meta.config['cache-buster']);
+ } else {
+ next();
+ }
+ });
// DEPRECATED
app.use(relativePath + '/api/language', function (req, res) {
winston.warn('[deprecated] Accessing language files from `/api/language` is deprecated. ' +
- 'Use `/assets/language/[langCode]/[namespace].json` for prefetch paths.');
+ 'Use `/assets/language' + req.path + '.json` for prefetch paths.');
res.redirect(relativePath + '/assets/language' + req.path + '.json?' + meta.config['cache-buster']);
});
- app.use(relativePath, express.static(path.join(__dirname, '../../', 'public'), {
- maxAge: app.enabled('cache') ? 5184000000 : 0
- }));
- app.use(relativePath + '/vendor/jquery/timeago/locales', middleware.processTimeagoLocales);
+ app.use(relativePath + '/assets/vendor/jquery/timeago/locales', middleware.processTimeagoLocales);
app.use(controllers.handle404);
app.use(controllers.handleURIErrors);
app.use(controllers.handleErrors);
diff --git a/src/routes/meta.js b/src/routes/meta.js
index c62465c8d0..af4e89d897 100644
--- a/src/routes/meta.js
+++ b/src/routes/meta.js
@@ -5,31 +5,6 @@ var nconf = require('nconf');
var meta = require('../meta');
-
-function sendMinifiedJS(req, res) {
- var target = path.basename(req.path);
- var cache = meta.js.target[target] ? meta.js.target[target].cache : '';
- res.type('text/javascript').send(cache);
-}
-
-// The portions of code involving the source map are commented out as they're broken in UglifyJS2
-// Follow along here: https://github.com/mishoo/UglifyJS2/issues/700
-// function sendJSSourceMap(req, res) {
-// if (meta.js.hasOwnProperty('map')) {
-// res.type('application/json').send(meta.js.map);
-// } else {
-// res.redirect(404);
-// }
-// };
-
-function sendStylesheet(req, res) {
- res.type('text/css').status(200).send(meta.css.cache);
-}
-
-function sendACPStylesheet(req, res) {
- res.type('text/css').status(200).send(meta.css.acpCache);
-}
-
function sendSoundFile(req, res, next) {
var resolved = meta.sounds._filePathHash[path.basename(req.path)];
@@ -41,11 +16,6 @@ function sendSoundFile(req, res, next) {
}
module.exports = function (app, middleware, controllers) {
- app.get('/stylesheet.css', middleware.addExpiresHeaders, sendStylesheet);
- app.get('/admin.css', middleware.addExpiresHeaders, sendACPStylesheet);
- app.get('/nodebb.min.js', middleware.addExpiresHeaders, sendMinifiedJS);
- app.get('/acp.min.js', middleware.addExpiresHeaders, sendMinifiedJS);
- // app.get('/nodebb.min.js.map', middleware.addExpiresHeaders, sendJSSourceMap);
app.get('/sitemap.xml', controllers.sitemap.render);
app.get('/sitemap/pages.xml', controllers.sitemap.getPages);
app.get('/sitemap/categories.xml', controllers.sitemap.getCategories);
diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl
index e6e92490f9..23ce1e2c02 100644
--- a/src/views/admin/header.tpl
+++ b/src/views/admin/header.tpl
@@ -4,8 +4,8 @@
{title}
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/admin/settings/group.tpl b/src/views/admin/settings/group.tpl
index 56a8b2c18e..1c0b660361 100644
--- a/src/views/admin/settings/group.tpl
+++ b/src/views/admin/settings/group.tpl
@@ -43,7 +43,7 @@
[[admin/settings/group:default-cover-help]]
-
+
diff --git a/src/views/admin/settings/uploads.tpl b/src/views/admin/settings/uploads.tpl
index 2e79da9a2b..6c9b59db80 100644
--- a/src/views/admin/settings/uploads.tpl
+++ b/src/views/admin/settings/uploads.tpl
@@ -131,7 +131,7 @@
[[admin/settings/uploads:default-covers-help]]
-
+
diff --git a/src/webserver.js b/src/webserver.js
index fec42974c6..2e7afdb228 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -88,7 +88,6 @@ function initializeNodeBB(callback) {
plugins.init(app, middleware, next);
},
async.apply(plugins.fireHook, 'static:assets.prepare', {}),
- async.apply(meta.js.bridgeModules, app),
function (next) {
plugins.fireHook('static:app.preload', {
app: app,
@@ -104,9 +103,6 @@ function initializeNodeBB(callback) {
},
function (next) {
async.series([
- async.apply(meta.js.getFromFile, 'nodebb.min.js'),
- async.apply(meta.js.getFromFile, 'acp.min.js'),
- async.apply(meta.css.getFromFile),
async.apply(meta.sounds.init),
async.apply(languages.init),
async.apply(meta.blacklist.load)
diff --git a/src/widgets/admin.js b/src/widgets/admin.js
index fc380804c6..ecfd73b750 100644
--- a/src/widgets/admin.js
+++ b/src/widgets/admin.js
@@ -3,6 +3,7 @@
var fs = require('fs');
var path = require('path');
var async = require('async');
+var nconf = require('nconf');
var plugins = require('../plugins');
var admin = {};
@@ -25,7 +26,7 @@ admin.get = function (callback) {
plugins.fireHook('filter:widgets.getWidgets', [], next);
},
adminTemplate: function (next) {
- fs.readFile(path.resolve(__dirname, '../../public/templates/admin/partials/widget-settings.tpl'), 'utf8', next);
+ fs.readFile(path.resolve(nconf.get('views_dir'), 'admin/partials/widget-settings.tpl'), 'utf8', next);
}
}, function (err, widgetData) {
if (err) {
diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js
index 44de8c7122..182d9fea12 100644
--- a/test/mocks/databasemock.js
+++ b/test/mocks/databasemock.js
@@ -20,7 +20,7 @@
base_dir: path.join(__dirname,'../..'),
themes_path: path.join(__dirname, '../../node_modules'),
upload_url: path.join(path.sep, '../../uploads', path.sep),
- views_dir: path.join(__dirname, '../../public/templates'),
+ views_dir: path.join(__dirname, '../../build/public/templates'),
relative_path: ''
});
From 8c86b2e32c9ebac4e9bbbfe2b8cb95e9d5e01f9c Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sun, 15 Jan 2017 12:38:16 -0700
Subject: [PATCH 02/10] Build pipeline improvements
- Refactor meta/css
- `fs.link` usage consolidated to `file.link`
- rimraf built modules directory to fix error
- Remove `local-assets` flag
---
build.js | 10 ++-
src/controllers/admin/uploads.js | 6 +-
src/file.js | 8 +++
src/meta/css.js | 102 +++++++++++++++----------------
src/meta/js.js | 24 ++------
src/meta/sounds.js | 22 +------
src/plugins/load.js | 3 +-
src/routes/index.js | 3 +-
src/routes/meta.js | 19 ------
src/webserver.js | 2 +-
10 files changed, 78 insertions(+), 121 deletions(-)
diff --git a/build.js b/build.js
index 32359e14f8..a8ad5007d1 100644
--- a/build.js
+++ b/build.js
@@ -46,7 +46,11 @@ exports.buildTargets = function (targets, callback) {
var meta = require('./src/meta');
buildStart = buildStart || Date.now();
- var step = function (startTime, target, next) {
+ var step = function (startTime, target, next, err) {
+ if (err) {
+ winston.error('Build failed: ' + err.message);
+ process.exit(1);
+ }
winston.info('[build] ' + target + ' => Completed in ' + ((Date.now() - startTime) / 1000) + 's');
next();
};
@@ -75,13 +79,13 @@ exports.buildTargets = function (targets, callback) {
case 'clientCSS':
winston.info('[build] Building client-side CSS');
startTime = Date.now();
- meta.css.minify('stylesheet.css', step.bind(this, startTime, target, next));
+ meta.css.minify('client', step.bind(this, startTime, target, next));
break;
case 'acpCSS':
winston.info('[build] Building admin control panel CSS');
startTime = Date.now();
- meta.css.minify('admin.css', step.bind(this, startTime, target, next));
+ meta.css.minify('admin', step.bind(this, startTime, target, next));
break;
case 'tpl':
diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js
index 2b1a60e281..25b2ae34fd 100644
--- a/src/controllers/admin/uploads.js
+++ b/src/controllers/admin/uploads.js
@@ -108,11 +108,7 @@ uploadsController.uploadSound = function (req, res, next) {
var soundsPath = path.join(__dirname, '../../../build/public/sounds'),
filePath = path.join(__dirname, '../../../public/uploads/sounds', uploadedFile.name);
- if (process.platform === 'win32') {
- fs.link(filePath, path.join(soundsPath, path.basename(filePath)));
- } else {
- fs.symlink(filePath, path.join(soundsPath, path.basename(filePath)), 'file');
- }
+ file.link(filePath, path.join(soundsPath, path.basename(filePath)));
fs.unlink(uploadedFile.path, function (err) {
if (err) {
diff --git a/src/file.js b/src/file.js
index df820cc47f..65f86a0bdd 100644
--- a/src/file.js
+++ b/src/file.js
@@ -102,4 +102,12 @@ file.existsSync = function (path) {
return !!exists;
};
+file.link = function link(filePath, destPath, cb) {
+ if (process.platform === 'win32') {
+ fs.link(filePath, destPath, cb);
+ } else {
+ fs.symlink(filePath, destPath, 'file', cb);
+ }
+};
+
module.exports = file;
diff --git a/src/meta/css.js b/src/meta/css.js
index f72ef6004d..d779ff8018 100644
--- a/src/meta/css.js
+++ b/src/meta/css.js
@@ -18,8 +18,32 @@ var utils = require('../../public/src/utils');
module.exports = function (Meta) {
Meta.css = {};
- Meta.css.cache = undefined;
- Meta.css.acpCache = undefined;
+
+ var buildImports = {
+ client: function (source) {
+ return '@import "./theme";\n' + source + '\n' + [
+ '@import "font-awesome";',
+ '@import (inline) "../../public/vendor/jquery/css/smoothness/jquery-ui.css";',
+ '@import (inline) "../../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";',
+ '@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";',
+ '@import "../../public/less/flags.less";',
+ '@import "../../public/less/blacklist.less";',
+ '@import "../../public/less/generics.less";',
+ '@import "../../public/less/mixins.less";',
+ '@import "../../public/less/global.less";',
+ ].map(function (str) { return str.replace(/\//g, path.sep); }).join('\n');
+ },
+ admin: function (source) {
+ return source + '\n' + [
+ '@import "font-awesome";',
+ '@import "../public/less/admin/admin";',
+ '@import "../public/less/generics.less";',
+ '@import (inline) "../public/vendor/colorpicker/colorpicker.css";',
+ '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";',
+ '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";',
+ ].map(function (str) { return str.replace(/\//g, path.sep); }).join('\n');
+ },
+ };
Meta.css.minify = function (target, callback) {
callback = callback || function () {};
@@ -30,25 +54,25 @@ module.exports = function (Meta) {
return callback(err);
}
- var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'),
- 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'),
- path.join(__dirname, '../../public/vendor/fontawesome/less')
- ],
- source = '@import "font-awesome";';
+ var themeId = (themeData['theme:id'] || 'nodebb-theme-persona');
+ var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla'));
+ var paths = [
+ baseThemePath,
+ path.join(__dirname, '../../node_modules'),
+ path.join(__dirname, '../../public/vendor/fontawesome/less')
+ ];
+ var source = '';
- plugins.lessFiles = filterMissingFiles(plugins.lessFiles);
- plugins.cssFiles = filterMissingFiles(plugins.cssFiles);
+ var lessFiles = filterMissingFiles(plugins.lessFiles);
+ var cssFiles = filterMissingFiles(plugins.cssFiles);
async.waterfall([
function (next) {
- getStyleSource(plugins.cssFiles, '\n@import (inline) ".', '.css', next);
+ getStyleSource(cssFiles, '\n@import (inline) ".', '.css', next);
},
function (src, next) {
source += src;
- getStyleSource(plugins.lessFiles, '\n@import ".', '.less', next);
+ getStyleSource(lessFiles, '\n@import ".', '.less', next);
},
function (src, next) {
source += src;
@@ -59,29 +83,7 @@ module.exports = function (Meta) {
return callback(err);
}
- var acpSource = source;
-
- if (target !== 'admin.css') {
- source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui.css";';
- source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";';
- source += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";';
- source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/flags.less";';
- source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";';
- source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";';
- source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";';
- source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/global.less";';
- source = '@import "./theme";\n' + source;
-
- minify(source, paths, 'cache', callback);
- } else {
- acpSource += '\n@import "..' + path.sep + 'public/less/admin/admin";\n';
- acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";\n';
- acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";\n';
- acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui.css";';
- acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";';
-
- minify(acpSource, paths, 'acpCache', callback);
- }
+ minify(buildImports[target](source), paths, target, callback);
});
});
};
@@ -115,22 +117,22 @@ module.exports = function (Meta) {
});
}
- Meta.css.commitToFile = function (filename, callback) {
- var file = (filename === 'acpCache' ? 'admin' : 'stylesheet') + '.css';
+ Meta.css.commitToFile = function (target, source, callback) {
+ var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css';
- fs.writeFile(path.join(__dirname, '../../build/public/' + file), Meta.css[filename], function (err) {
+ fs.writeFile(path.join(__dirname, '../../build/public/' + filename), source, function (err) {
if (!err) {
- winston.verbose('[meta/css] ' + file + ' committed to disk.');
+ winston.verbose('[meta/css] ' + target + ' CSS committed to disk.');
} else {
winston.error('[meta/css] ' + err.message);
- process.exit(0);
+ process.exit(1);
}
callback();
});
};
- function minify(source, paths, destination, callback) {
+ function minify(source, paths, target, callback) {
callback = callback || function () {};
less.render(source, {
paths: paths
@@ -140,20 +142,14 @@ module.exports = function (Meta) {
return callback(err);
}
- postcss([ autoprefixer, clean() ]).process(lessOutput.css).then(function (result) {
+ postcss(global.env === 'development' ? [ autoprefixer ] : [ autoprefixer, clean() ]).process(lessOutput.css).then(function (result) {
result.warnings().forEach(function (warn) {
winston.verbose(warn.toString());
});
- Meta.css[destination] = result.css;
- // Save the compiled CSS in public/ so things like nginx can serve it
- if (nconf.get('local-assets') === undefined || nconf.get('local-assets') !== false) {
- return Meta.css.commitToFile(destination, function () {
- callback(null, result.css);
- });
- }
-
- callback(null, result.css);
+ return Meta.css.commitToFile(target, result.css, function () {
+ callback(null, result.css);
+ });
});
});
}
diff --git a/src/meta/js.js b/src/meta/js.js
index 8c3794043f..d479e9161c 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -4,9 +4,11 @@ var winston = require('winston');
var fork = require('child_process').fork;
var path = require('path');
var async = require('async');
-var nconf = require('nconf');
var fs = require('fs');
var mkdirp = require('mkdirp');
+var rimraf = require('rimraf');
+
+var file = require('../file');
var plugins = require('../plugins');
var utils = require('../../public/src/utils');
@@ -88,19 +90,10 @@ module.exports = function (Meta) {
};
Meta.js.linkModules = function (callback) {
- function link(filePath, destPath, cb) {
- if (process.platform === 'win32') {
- fs.link(filePath, destPath, cb);
- } else {
- fs.symlink(filePath, destPath, 'file', cb);
- }
- }
-
- plugins.reload(function (err) {
+ rimraf(path.join(__dirname, '../../build/public/src/modules'), function (err) {
if (err) {
return callback(err);
}
-
async.each(Object.keys(Meta.js.scripts.modules), function (relPath, next) {
var filePath = path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]);
var destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
@@ -110,7 +103,7 @@ module.exports = function (Meta) {
return next(err);
}
- link(filePath, destPath, next);
+ file.link(filePath, destPath, next);
});
}, callback);
});
@@ -143,12 +136,7 @@ module.exports = function (Meta) {
winston.verbose('[meta/js] ' + target + ' minification complete');
minifier.kill();
- if (nconf.get('local-assets') === undefined || nconf.get('local-assets') !== false) {
- return Meta.js.commitToFile(target, callback);
- } else {
- return callback();
- }
-
+ Meta.js.commitToFile(target, callback);
break;
case 'error':
winston.error('[meta/js] Could not compile ' + target + ': ' + message.message);
diff --git a/src/meta/sounds.js b/src/meta/sounds.js
index 6acbd77a1f..e44f43cccc 100644
--- a/src/meta/sounds.js
+++ b/src/meta/sounds.js
@@ -8,6 +8,7 @@ var rimraf = require('rimraf');
var mkdirp = require('mkdirp');
var async = require('async');
+var file = require('../file');
var plugins = require('../plugins');
var db = require('../database');
@@ -107,21 +108,6 @@ module.exports = function (Meta) {
return;
}
- if (nconf.get('local-assets') === false) {
- // Don't regenerate the public/sounds/ directory. Instead, create a mapping for the router to use
- Meta.sounds._filePathHash = filePaths.reduce(function (hash, filePath) {
- hash[path.basename(filePath)] = filePath;
- return hash;
- }, {});
-
- winston.verbose('[sounds] Sounds OK');
- if (typeof next === 'function') {
- return next();
- } else {
- return;
- }
- }
-
// Clear the sounds directory
async.series([
function (next) {
@@ -138,11 +124,7 @@ module.exports = function (Meta) {
// Link paths
async.each(filePaths, function (filePath, next) {
- if (process.platform === 'win32') {
- fs.link(filePath, path.join(soundsPath, path.basename(filePath)), next);
- } else {
- fs.symlink(filePath, path.join(soundsPath, path.basename(filePath)), 'file', next);
- }
+ file.link(filePath, path.join(soundsPath, path.basename(filePath)), next);
}, function (err) {
if (!err) {
winston.verbose('[sounds] Sounds OK');
diff --git a/src/plugins/load.js b/src/plugins/load.js
index 3121f8457e..cf6e0b4780 100644
--- a/src/plugins/load.js
+++ b/src/plugins/load.js
@@ -54,7 +54,8 @@ module.exports = function (Plugins) {
async.parallel([
async.apply(mapFiles, pluginData, 'css', 'cssFiles'),
async.apply(mapFiles, pluginData, 'less', 'lessFiles'),
- async.apply(mapClientSideScripts, pluginData)
+ async.apply(mapClientSideScripts, pluginData),
+ async.apply(mapClientModules, pluginData),
], next);
}, next);
}
diff --git a/src/routes/index.js b/src/routes/index.js
index e1f95891cf..506a0063ec 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -167,7 +167,8 @@ module.exports = function (app, middleware, hotswapIds) {
'/templates/',
'/src/',
'/images/',
- // '/sounds/',
+ '/language/',
+ '/sounds/',
];
app.use(relativePath, function (req, res, next) {
if (deprecatedPaths.some(function (path) { return req.path.startsWith(path); })) {
diff --git a/src/routes/meta.js b/src/routes/meta.js
index af4e89d897..cb089f1200 100644
--- a/src/routes/meta.js
+++ b/src/routes/meta.js
@@ -1,20 +1,5 @@
"use strict";
-var path = require('path');
-var nconf = require('nconf');
-
-var meta = require('../meta');
-
-function sendSoundFile(req, res, next) {
- var resolved = meta.sounds._filePathHash[path.basename(req.path)];
-
- if (resolved) {
- res.status(200).sendFile(resolved);
- } else {
- next();
- }
-}
-
module.exports = function (app, middleware, controllers) {
app.get('/sitemap.xml', controllers.sitemap.render);
app.get('/sitemap/pages.xml', controllers.sitemap.getPages);
@@ -23,8 +8,4 @@ module.exports = function (app, middleware, controllers) {
app.get('/robots.txt', controllers.robots);
app.get('/manifest.json', controllers.manifest);
app.get('/css/previews/:theme', controllers.admin.themes.get);
-
- if (nconf.get('local-assets') === false) {
- app.get('/sounds/*', middleware.addExpiresHeaders, sendSoundFile);
- }
};
diff --git a/src/webserver.js b/src/webserver.js
index 2e7afdb228..a8324f7376 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -45,7 +45,7 @@ server.on('error', function (err) {
winston.error(err);
if (err.code === 'EADDRINUSE') {
winston.error('NodeBB address in use, exiting...');
- process.exit(0);
+ process.exit(1);
} else {
throw err;
}
From 186209a0a3e75914052e400ca2408c9052b05fe9 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 21 Jan 2017 18:57:27 -0700
Subject: [PATCH 03/10] Build plugin `staticDirs`
- route `/plugins` -> `build/public/plugins`
- symlink `staticDirs` directories into `build/public/plugins`
- remove custom `/plugins` route handling
---
build.js | 1 +
src/file.js | 5 +++++
src/meta/js.js | 22 +++++++++++++++++++++-
src/plugins/load.js | 2 ++
src/routes/index.js | 6 ++++--
src/routes/plugins.js | 40 ----------------------------------------
6 files changed, 33 insertions(+), 43 deletions(-)
delete mode 100644 src/routes/plugins.js
diff --git a/build.js b/build.js
index a8ad5007d1..73013231d5 100644
--- a/build.js
+++ b/build.js
@@ -62,6 +62,7 @@ exports.buildTargets = function (targets, callback) {
var startTime = Date.now();
async.series([
meta.js.linkModules,
+ meta.js.linkStatics,
async.apply(meta.js.minify, 'nodebb.min.js'),
async.apply(meta.js.minify, 'acp.min.js')
], step.bind(this, startTime, 'js', next));
diff --git a/src/file.js b/src/file.js
index 65f86a0bdd..d8fa128e5e 100644
--- a/src/file.js
+++ b/src/file.js
@@ -110,4 +110,9 @@ file.link = function link(filePath, destPath, cb) {
}
};
+file.linkDirs = function linkDirs(sourceDir, destDir, callback) {
+ var type = (process.platform === 'win32') ? 'junction' : 'dir';
+ fs.symlink(sourceDir, destDir, type, callback);
+};
+
module.exports = file;
diff --git a/src/meta/js.js b/src/meta/js.js
index d479e9161c..a44d18baba 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -94,7 +94,7 @@ module.exports = function (Meta) {
if (err) {
return callback(err);
}
- async.each(Object.keys(Meta.js.scripts.modules), function (relPath, next) {
+ async.eachLimit(Object.keys(Meta.js.scripts.modules), 1000, function (relPath, next) {
var filePath = path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]);
var destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
@@ -109,6 +109,26 @@ module.exports = function (Meta) {
});
};
+ Meta.js.linkStatics = function (callback) {
+ rimraf(path.join(__dirname, '../../build/public/plugins'), function (err) {
+ if (err) {
+ return callback(err);
+ }
+ async.eachLimit(Object.keys(plugins.staticDirs), 1000, function (mappedPath, next) {
+ var sourceDir = plugins.staticDirs[mappedPath];
+ var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath);
+
+ mkdirp(path.dirname(destDir), function (err) {
+ if (err) {
+ return next(err);
+ }
+
+ file.linkDirs(sourceDir, destDir, next);
+ });
+ }, callback);
+ });
+ };
+
Meta.js.minify = function (target, callback) {
winston.verbose('[meta/js] Minifying ' + target);
diff --git a/src/plugins/load.js b/src/plugins/load.js
index cf6e0b4780..01dedc4e45 100644
--- a/src/plugins/load.js
+++ b/src/plugins/load.js
@@ -56,6 +56,7 @@ module.exports = function (Plugins) {
async.apply(mapFiles, pluginData, 'less', 'lessFiles'),
async.apply(mapClientSideScripts, pluginData),
async.apply(mapClientModules, pluginData),
+ async.apply(mapStaticDirectories, pluginData, pluginData.path),
], next);
}, next);
}
@@ -299,6 +300,7 @@ module.exports = function (Plugins) {
pluginData.version = packageData.version;
pluginData.repository = packageData.repository;
pluginData.nbbpm = packageData.nbbpm;
+ pluginData.path = pluginPath;
} catch(err) {
var pluginDir = pluginPath.split(path.sep);
pluginDir = pluginDir[pluginDir.length - 1];
diff --git a/src/routes/index.js b/src/routes/index.js
index 506a0063ec..0b9e6fce9a 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -15,7 +15,6 @@ var metaRoutes = require('./meta');
var apiRoutes = require('./api');
var adminRoutes = require('./admin');
var feedRoutes = require('./feeds');
-var pluginRoutes = require('./plugins');
var authRoutes = require('./authentication');
var helpers = require('./helpers');
@@ -123,7 +122,6 @@ module.exports = function (app, middleware, hotswapIds) {
metaRoutes(router, middleware, controllers);
apiRoutes(router, middleware, controllers);
feedRoutes(router, middleware, controllers);
- pluginRoutes(router, middleware, controllers);
mainRoutes(router, middleware, controllers);
topicRoutes(router, middleware, controllers);
@@ -152,6 +150,10 @@ module.exports = function (app, middleware, hotswapIds) {
app.use(relativePath + '/assets', express.static(path.join(__dirname, '../../public'), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
+ // TODO: deprecate?
+ app.use(relativePath + '/plugins', express.static(path.join(__dirname, '../../build/public/plugins'), {
+ maxAge: app.enabled('cache') ? 5184000000 : 0
+ }));
// DEPRECATED
var deprecatedPaths = [
diff --git a/src/routes/plugins.js b/src/routes/plugins.js
deleted file mode 100644
index 37e23c5ee6..0000000000
--- a/src/routes/plugins.js
+++ /dev/null
@@ -1,40 +0,0 @@
-"use strict";
-
-var _ = require('underscore');
-var path = require('path');
-
-var plugins = require('../plugins');
-
-module.exports = function (app, middleware, controllers) {
- // Static Assets
- app.get('/plugins/:id/*', middleware.addExpiresHeaders, function (req, res, next) {
-
- var relPath = req._parsedUrl.pathname.replace('/plugins/', '');
-
- var matches = _.map(plugins.staticDirs, function (realPath, mappedPath) {
- if (relPath.match(mappedPath)) {
- var pathToFile = path.join(plugins.staticDirs[mappedPath], decodeURIComponent(relPath.slice(mappedPath.length)));
- if (pathToFile.startsWith(plugins.staticDirs[mappedPath])) {
- return pathToFile;
- }
- }
-
- return null;
- }).filter(Boolean);
-
- if (!matches || !matches.length) {
- return next();
- }
-
- res.sendFile(matches[0], {}, function (err) {
- if (err) {
- if (err.code === 'ENOENT') {
- // File doesn't exist, this isn't an error, to send to 404 handler
- return next();
- } else {
- return next(err);
- }
- }
- });
- });
-};
\ No newline at end of file
From 001aa3d1a85eb79cc04acae3626b710c9ea482b3 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 21 Jan 2017 19:21:02 -0700
Subject: [PATCH 04/10] Fix tests, npm Windows fixes
---
nodebb | 4 ++--
src/plugins/install.js | 2 +-
test/mocks/databasemock.js | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/nodebb b/nodebb
index e1a4ab7fbf..7f1d2941a3 100755
--- a/nodebb
+++ b/nodebb
@@ -225,12 +225,12 @@ var getRunningPid = function (callback) {
if (['y', 'Y', 'yes', 'YES'].indexOf(result.upgrade) !== -1) {
process.stdout.write('\nUpgrading packages...');
- var args = ['npm', 'i'];
+ var args = ['i'];
found.forEach(function (suggestObj) {
args.push(suggestObj.name + '@' + suggestObj.suggested);
});
- require('child_process').execFile('/usr/bin/env', args, { stdio: 'ignore' }, function (err) {
+ require('child_process').execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', args, { stdio: 'ignore' }, function (err) {
if (!err) {
process.stdout.write(' OK\n'.green);
}
diff --git a/src/plugins/install.js b/src/plugins/install.js
index a914a09ca6..840d46008a 100644
--- a/src/plugins/install.js
+++ b/src/plugins/install.js
@@ -102,7 +102,7 @@ module.exports = function (Plugins) {
}
function runNpmCommand(command, pkgName, version, callback) {
- require('child_process').execFile('npm', [command, pkgName + (command === 'install' ? '@' + version : '')], function (err, stdout) {
+ require('child_process').execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', [command, pkgName + (command === 'install' ? '@' + version : '')], function (err, stdout) {
if (err) {
return callback(err);
}
diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js
index 182d9fea12..b7ed4cfb8c 100644
--- a/test/mocks/databasemock.js
+++ b/test/mocks/databasemock.js
@@ -140,7 +140,7 @@
nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json'));
nconf.set('bcrypt_rounds', 1);
- require('../../build').buildTargets(['js', 'clientCSS', 'acpCSS', 'tpl'], next);
+ require('../../build').buildAll(next);
},
function (next) {
var webserver = require('../../src/webserver');
From f4147f7922e1f19b1f442546f2225b7bc3193b48 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Mon, 23 Jan 2017 21:06:34 -0700
Subject: [PATCH 05/10] Generate cache buster on build
---
build.js | 23 ++++++++++++++-------
src/meta/cacheBuster.js | 46 +++++++++++++++++++++++++++++++++++++++++
src/meta/configs.js | 14 +++++++++----
3 files changed, 72 insertions(+), 11 deletions(-)
create mode 100644 src/meta/cacheBuster.js
diff --git a/build.js b/build.js
index 73013231d5..89c55177ad 100644
--- a/build.js
+++ b/build.js
@@ -44,6 +44,8 @@ exports.build = function build(targets, callback) {
exports.buildTargets = function (targets, callback) {
var meta = require('./src/meta');
+ var cacheBuster = require('./src/meta/cacheBuster');
+
buildStart = buildStart || Date.now();
var step = function (startTime, target, next, err) {
@@ -114,14 +116,21 @@ exports.buildTargets = function (targets, callback) {
return process.exit(1);
}
- var time = (Date.now() - buildStart) / 1000;
+ cacheBuster.write(function (err) {
+ if (err) {
+ winston.error('[build] Failed to write `cache-buster.conf`: ' + err.message);
+ return process.exit(1);
+ }
- winston.info('[build] Asset compilation successful. Completed in ' + time + 's.');
+ var time = (Date.now() - buildStart) / 1000;
- if (typeof callback === 'function') {
- callback();
- } else {
- process.exit(0);
- }
+ winston.info('[build] Asset compilation successful. Completed in ' + time + 's.');
+
+ if (typeof callback === 'function') {
+ callback();
+ } else {
+ process.exit(0);
+ }
+ });
});
};
\ No newline at end of file
diff --git a/src/meta/cacheBuster.js b/src/meta/cacheBuster.js
new file mode 100644
index 0000000000..ca00fba057
--- /dev/null
+++ b/src/meta/cacheBuster.js
@@ -0,0 +1,46 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var mkdirp = require('mkdirp');
+var winston = require('winston');
+
+var filePath = path.join(__dirname, '../../build/cache-buster');
+
+var cached;
+
+// cache buster is an 11-character, lowercase, alphanumeric string
+function generate() {
+ return (Math.random() * 1e18).toString(32).slice(0, 11);
+}
+
+exports.write = function write(callback) {
+ mkdirp(path.dirname(filePath), function (err) {
+ if (err) {
+ return callback(err);
+ }
+
+ fs.writeFile(filePath, generate(), callback);
+ });
+};
+
+exports.read = function read(callback) {
+ if (cached) {
+ return callback(null, cached);
+ }
+
+ fs.readFile(filePath, function (err, buffer) {
+ if (err) {
+ winston.warn('[cache-buster] could not read cache buster: ' + err.message);
+ return callback();
+ }
+
+ buffer = buffer.toString();
+ if (buffer) {
+ cached = buffer;
+ return callback(null, cached);
+ }
+
+ callback();
+ });
+};
diff --git a/src/meta/configs.js b/src/meta/configs.js
index 75da0595d2..907e0ef477 100644
--- a/src/meta/configs.js
+++ b/src/meta/configs.js
@@ -6,7 +6,7 @@ var nconf = require('nconf');
var db = require('../database');
var pubsub = require('../pubsub');
-var utils = require('../../public/src/utils');
+var cacheBuster = require('./cacheBuster');
module.exports = function (Meta) {
@@ -21,10 +21,16 @@ module.exports = function (Meta) {
Meta.configs.list(next);
},
function (config, next) {
- config['cache-buster'] = 'v=' + utils.generateUUID();
+ cacheBuster.read(function (err, buster) {
+ if (err) {
+ return next(err);
+ }
+
+ config['cache-buster'] = 'v=' + (buster || Date.now());
- Meta.config = config;
- setImmediate(next);
+ Meta.config = config;
+ next();
+ });
}
], callback);
};
From 44a59ac435e18e62666821fbde21a69e72eaee68 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 3 Feb 2017 16:37:03 -0700
Subject: [PATCH 06/10] Fix tests to use default activated plugin
---
test/plugins.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/plugins.js b/test/plugins.js
index 09537d8990..06aabaab8d 100644
--- a/test/plugins.js
+++ b/test/plugins.js
@@ -152,7 +152,7 @@ describe('Plugins', function () {
});
describe('static assets', function () {
- it('should 404 if resource does not exist', function (done) {
+ it('should 404 if plugin does not exist', function (done) {
request.get(nconf.get('url') + '/plugins/doesnotexist/should404.tpl', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 404);
@@ -162,7 +162,7 @@ describe('Plugins', function () {
});
it('should 404 if resource does not exist', function (done) {
- request.get(nconf.get('url') + '/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl', function (err, res, body) {
+ request.get(nconf.get('url') + '/plugins/nodebb-plugin-markdown/js/should404.js', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 404);
assert(body);
@@ -171,7 +171,7 @@ describe('Plugins', function () {
});
it('should get resource', function (done) {
- request.get(nconf.get('url') + '/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/dbsearch.tpl', function (err, res, body) {
+ request.get(nconf.get('url') + '/plugins/nodebb-plugin-markdown/js/admin.js', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
From d31499a860b6e567b41c030132853eb1cb1a01b3 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 3 Feb 2017 18:29:09 -0700
Subject: [PATCH 07/10] Fix tests rewriting `logo.png`
---
src/image.js | 3 +++
src/user/picture.js | 3 ++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/image.js b/src/image.js
index 1cfbdccbe3..0ab68e5a23 100644
--- a/src/image.js
+++ b/src/image.js
@@ -65,6 +65,9 @@ image.resizeImage = function (data, callback) {
}
},
function (image, next) {
+ if (data.write === false) {
+ return next();
+ }
image.write(data.target || data.path, next);
}
], function (err) {
diff --git a/src/user/picture.js b/src/user/picture.js
index 0fd1d1a3bd..273a5dd413 100644
--- a/src/user/picture.js
+++ b/src/user/picture.js
@@ -57,7 +57,8 @@ module.exports = function (User) {
path: picture.path,
extension: extension,
width: imageDimension,
- height: imageDimension
+ height: imageDimension,
+ write: false,
}, next);
},
function (next) {
From fdad66f58e721b4a02b89f5d06d1b6c2889f27ef Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 3 Feb 2017 23:48:43 -0700
Subject: [PATCH 08/10] Revert "Fix tests to use default activated plugin"
This reverts commit 44a59ac435e18e62666821fbde21a69e72eaee68.
---
test/plugins.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/plugins.js b/test/plugins.js
index 06aabaab8d..09537d8990 100644
--- a/test/plugins.js
+++ b/test/plugins.js
@@ -152,7 +152,7 @@ describe('Plugins', function () {
});
describe('static assets', function () {
- it('should 404 if plugin does not exist', function (done) {
+ it('should 404 if resource does not exist', function (done) {
request.get(nconf.get('url') + '/plugins/doesnotexist/should404.tpl', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 404);
@@ -162,7 +162,7 @@ describe('Plugins', function () {
});
it('should 404 if resource does not exist', function (done) {
- request.get(nconf.get('url') + '/plugins/nodebb-plugin-markdown/js/should404.js', function (err, res, body) {
+ request.get(nconf.get('url') + '/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 404);
assert(body);
@@ -171,7 +171,7 @@ describe('Plugins', function () {
});
it('should get resource', function (done) {
- request.get(nconf.get('url') + '/plugins/nodebb-plugin-markdown/js/admin.js', function (err, res, body) {
+ request.get(nconf.get('url') + '/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/dbsearch.tpl', function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
From d43564dbbd31a8d7cd04cdcef0db80901035397b Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 3 Feb 2017 23:55:33 -0700
Subject: [PATCH 09/10] No need to build multiple times
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 1395ce329d..c43218397f 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"scripts": {
"start": "node loader.js",
"lint": "eslint --cache .",
- "pretest": "npm run lint && node app --build",
+ "pretest": "npm run lint",
"test": "istanbul cover node_modules/mocha/bin/_mocha -- -R dot",
"coveralls": "istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
},
From b530701cffd74c70f37225c598cd226f2a13b525 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Mon, 6 Feb 2017 15:54:21 -0700
Subject: [PATCH 10/10] Fix tabs :rage:
---
src/meta/css.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/meta/css.js b/src/meta/css.js
index 6c4a0f62e3..3627c6c41a 100644
--- a/src/meta/css.js
+++ b/src/meta/css.js
@@ -26,7 +26,7 @@ module.exports = function (Meta) {
'@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";',
'@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";',
'@import (inline) "../public/vendor/colorpicker/colorpicker.css";',
- '@import (inline) "../node_modules/cropperjs/dist/cropper.css";',
+ '@import (inline) "../node_modules/cropperjs/dist/cropper.css";',
'@import "../../public/less/flags.less";',
'@import "../../public/less/blacklist.less";',
'@import "../../public/less/generics.less";',