Highlighted routes, controllers & renderers

refs #5091, refs #9192

- There are several theme template "renderers" all over the codebase
- Some are in apps, and were called "controllers"
- One is in error handling
- All of them now have comments marking out how they share logic/steps
- Other comments describe routes & controllers where they live
This commit is contained in:
Hannah Wolfe 2017-11-05 12:45:43 +00:00
parent 474e9234a6
commit abaf0461cf
16 changed files with 156 additions and 80 deletions

View File

@ -7,14 +7,16 @@ var router = require('./lib/router'),
module.exports = {
activate: function activate(ghost) {
registerHelpers(ghost);
var ampRoute = '*/' + config.get('routeKeywords').amp + '/';
ghost.routeService.registerRouter('*/' + config.get('routeKeywords').amp + '/', function settingsEnabledRouter(req, res, next) {
ghost.routeService.registerRouter(ampRoute, function settingsEnabledRouter(req, res, next) {
if (settingsCache.get('amp') === true) {
return router.apply(this, arguments);
}
next();
});
registerHelpers(ghost);
}
};

View File

@ -7,29 +7,38 @@ var path = require('path'),
errors = require('../../../errors'),
templates = require('../../../controllers/frontend/templates'),
postLookup = require('../../../controllers/frontend/post-lookup'),
setResponseContext = require('../../../controllers/frontend/context');
setResponseContext = require('../../../controllers/frontend/context'),
function controller(req, res, next) {
var templateName = 'amp',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
view = templates.pickTemplate(templateName, defaultTemplate),
data = req.body || {};
templateName = 'amp',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs');
function _renderer(req, res, next) {
// Renderer begin
// Format data
var data = req.body || {};
// CASE: we only support amp pages for posts that are not static pages
if (!data.post || data.post.page) {
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
}
// Context
setResponseContext(req, res, data);
return res.render(view, data);
// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);
// Render Call
return res.render(res.template, data);
}
// This here is a controller.
// In fact, this whole file is nothing more than a controller + renderer & doesn't need to be a router
function getPostData(req, res, next) {
req.body = req.body || {};
postLookup(res.locals.relativeUrl)
.then(function (result) {
.then(function handleResult(result) {
if (result && result.post) {
req.body.post = result.post;
}
@ -43,9 +52,9 @@ function getPostData(req, res, next) {
ampRouter.route('/')
.get(
getPostData,
controller
_renderer
);
module.exports = ampRouter;
module.exports.controller = controller;
module.exports.renderer = _renderer;
module.exports.getPostData = getPostData;

View File

@ -5,28 +5,35 @@ var config = require('../../config'),
i18n = require('../../i18n'),
middleware = require('./lib/middleware'),
router = require('./lib/router'),
registerHelpers = require('./lib/helpers');
registerHelpers = require('./lib/helpers'),
checkSubdir;
checkSubdir = function checkSubdir() {
var paths;
if (utils.url.getSubdir()) {
paths = utils.url.getSubdir().split('/');
if (paths.pop() === config.get('routeKeywords').private) {
logging.error(new errors.GhostError({
message: i18n.t('errors.config.urlCannotContainPrivateSubdir.error'),
context: i18n.t('errors.config.urlCannotContainPrivateSubdir.description'),
help: i18n.t('errors.config.urlCannotContainPrivateSubdir.help')
}));
// @TODO: why
process.exit(0);
}
}
};
module.exports = {
activate: function activate(ghost) {
var paths;
var privateRoute = '/' + config.get('routeKeywords').private + '/';
if (utils.url.getSubdir()) {
paths = utils.url.getSubdir().split('/');
checkSubdir();
if (paths.pop() === config.get('routeKeywords').private) {
logging.error(new errors.GhostError({
message: i18n.t('errors.config.urlCannotContainPrivateSubdir.error'),
context: i18n.t('errors.config.urlCannotContainPrivateSubdir.description'),
help: i18n.t('errors.config.urlCannotContainPrivateSubdir.help')
}));
// @TODO: why
process.exit(0);
}
}
ghost.routeService.registerRouter('/' + config.get('routeKeywords').private + '/', router);
ghost.routeService.registerRouter(privateRoute, router);
registerHelpers(ghost);
},

View File

@ -6,36 +6,43 @@ var path = require('path'),
setResponseContext = require('../../../controllers/frontend/context'),
brute = require('../../../middleware/brute'),
templateName = 'private',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
privateRouter = express.Router();
function controller(req, res) {
var templateName = 'private',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
view = templates.pickTemplate(templateName, defaultTemplate),
data = {};
function _renderer(req, res) {
// Renderer begin
// Format data
var data = {};
if (res.error) {
data.error = res.error;
}
// Context
setResponseContext(req, res);
return res.render(view, data);
// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);
// Render Call
return res.render(res.template, data);
}
// password-protected frontend route
privateRouter.route('/')
.get(
middleware.isPrivateSessionAuth,
controller
_renderer
)
.post(
bodyParser.urlencoded({extended: true}),
middleware.isPrivateSessionAuth,
brute.privateBlog,
middleware.authenticateProtection,
controller
_renderer
);
module.exports = privateRouter;
module.exports.controller = controller;
module.exports.renderer = _renderer;

View File

@ -7,10 +7,11 @@ var router = require('./lib/router'),
module.exports = {
activate: function activate(ghost) {
var subscribeRoute = '/' + config.get('routeKeywords').subscribe + '/';
// TODO, how to do all this only if the Subscribers flag is set?!
registerHelpers(ghost);
ghost.routeService.registerRouter('/' + config.get('routeKeywords').subscribe + '/', function labsEnabledRouter(req, res, next) {
ghost.routeService.registerRouter(subscribeRoute, function labsEnabledRouter(req, res, next) {
if (labs.isSet('subscribers') === true) {
return router.apply(this, arguments);
}

View File

@ -10,17 +10,25 @@ var path = require('path'),
validator = require('../../../data/validation').validator,
templates = require('../../../controllers/frontend/templates'),
postLookup = require('../../../controllers/frontend/post-lookup'),
setResponseContext = require('../../../controllers/frontend/context');
setResponseContext = require('../../../controllers/frontend/context'),
function controller(req, res) {
var templateName = 'subscribe',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
view = templates.pickTemplate(templateName, defaultTemplate),
data = req.body;
templateName = 'subscribe',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs');
// In future we'd have a more complex controller here - showing if someone already subscribed?!
function _renderer(req, res) {
// Renderer begin
// Format data
var data = req.body;
// Context
setResponseContext(req, res);
return res.render(view, data);
// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);
// Render Call
return res.render(res.template, data);
}
/**
@ -33,7 +41,7 @@ function errorHandler(error, req, res, next) {
if (error.statusCode !== 404) {
res.locals.error = error;
return controller(req, res);
return _renderer(req, res);
}
next(error);
@ -100,19 +108,18 @@ function storeSubscriber(req, res, next) {
// subscribe frontend route
subscribeRouter.route('/')
.get(
controller
_renderer
)
.post(
bodyParser.urlencoded({extended: true}),
honeyPot,
handleSource,
storeSubscriber,
controller
_renderer
);
// configure an error handler just for subscribe problems
subscribeRouter.use(errorHandler);
module.exports = subscribeRouter;
module.exports.controller = controller;
module.exports.storeSubscriber = storeSubscriber;

View File

@ -8,6 +8,9 @@ var _ = require('lodash'),
setRequestIsSecure = require('./frontend/secure'),
renderChannel = require('./frontend/render-channel');
// This here is a controller.
// The "route" is handled in controllers/channels/router.js
// There's both a top-level channelS router, and an individual channel one
module.exports = function channelController(req, res, next) {
// Parse the parameters we need from the URL
var pageParam = req.params.page !== undefined ? req.params.page : 1,

View File

@ -6,12 +6,19 @@ var debug = require('ghost-ignition').debug('channels:render'),
module.exports = function renderChannel(req, res) {
debug('renderChannel called');
return function renderChannel(result) {
var view = templates.channel(res.locals.channel);
// Renderer begin
// Format data 2
// Do final data formatting and then render
result = formatResponse.channel(result);
// Context
setResponseContext(req, res);
debug('Rendering view: ' + view);
res.render(view, result);
// Template
res.template = templates.channel(res.locals.channel);
// Render Call
debug('Rendering view: ' + res.template);
res.render(res.template, result);
};
};

View File

@ -12,11 +12,18 @@ var debug = require('ghost-ignition').debug('channels:render-post'),
module.exports = function renderPost(req, res) {
debug('renderPost called');
return function renderPost(post) {
var view = templates.single(post),
response = formatResponse.single(post);
// Renderer begin
// Format data 2 - 1 is in preview/single
var response = formatResponse.single(post);
// Context
setResponseContext(req, res, response);
debug('Rendering view: ' + view);
res.render(view, response);
// Template
res.template = templates.single(post);
// Render Call
debug('Rendering view: ' + res.template);
res.render(res.template, response);
};
};

View File

@ -5,6 +5,8 @@ var api = require('../api'),
renderPost = require('./frontend/render-post'),
setRequestIsSecure = require('./frontend/secure');
// This here is a controller.
// The "route" is handled in site/routes.js
module.exports = function previewController(req, res, next) {
var params = {
uuid: req.params.uuid,
@ -13,6 +15,7 @@ module.exports = function previewController(req, res, next) {
};
api.posts.read(params).then(function then(result) {
// Format data 1
var post = result.posts[0];
if (!post) {

View File

@ -46,6 +46,9 @@ function getData(channelOpts) {
});
}
// This here is a controller.
// The "route" is handled in controllers/channels/router.js
// We can only generate RSS for channels, so that sorta makes sense, but the location is rubbish
// @TODO finish refactoring this - it's now a controller
generate = function generate(req, res, next) {
// Parse the parameters we need from the URL
@ -67,11 +70,17 @@ generate = function generate(req, res, next) {
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
}
// Renderer begin
// Format data
data.version = res.locals.safeVersion;
data.siteUrl = utils.url.urlFor('home', {secure: req.secure}, true);
data.feedUrl = utils.url.urlFor({relativeUrl: baseUrl, secure: req.secure}, true);
data.secure = req.secure;
// No context, no template
// @TODO: should we have context? The context file expects it!
// Render call - to a different renderer
// @TODO this is effectively a renderer
return rssCache.getXML(baseUrl, data).then(function then(feedXml) {
res.set('Content-Type', 'text/xml; charset=UTF-8');

View File

@ -5,9 +5,12 @@ var utils = require('../utils'),
renderPost = require('./frontend/render-post'),
setRequestIsSecure = require('./frontend/secure');
// This here is a controller.
// The "route" is handled in site/routes.js
module.exports = function singleController(req, res, next) {
// Query database to find post
return postLookup(req.path).then(function then(lookup) {
// Format data 1
var post = lookup ? lookup.post : false;
if (!post) {

View File

@ -71,6 +71,7 @@ _private.JSONErrorRenderer = function JSONErrorRenderer(err, req, res, next) { /
});
};
// @TODO: differenciate properly between rendering errors for theme templates, and other situations
_private.HTMLErrorRenderer = function HTMLErrorRender(err, req, res, next) {
// If the error code is explicitly set to STATIC_FILE_NOT_FOUND,
// Skip trying to render an HTML error, and move on to the basic error renderer
@ -80,25 +81,34 @@ _private.HTMLErrorRenderer = function HTMLErrorRender(err, req, res, next) {
return next(err);
}
// Renderer begin
// Format Data
var templateData = {
message: err.message,
// @deprecated
code: err.statusCode,
statusCode: err.statusCode,
errorDetails: err.errorDetails || []
},
template = templates.error(err.statusCode);
message: err.message,
// @deprecated
code: err.statusCode,
statusCode: err.statusCode,
errorDetails: err.errorDetails || []
};
// Context
// We don't do context for errors?!
// Template
res.template = templates.error(err.statusCode);
// It can be that something went wrong with the theme or otherwise loading handlebars
// This ensures that no matter what res.render will work here
// @TODO: split the error handler for assets, admin & theme to refactor this away
if (_.isEmpty(req.app.engines)) {
template = 'error';
res.template = 'error';
req.app.engine('hbs', _private.createHbsEngine());
req.app.set('view engine', 'hbs');
req.app.set('views', config.get('paths').defaultViews);
}
res.render(template, templateData, function renderResponse(err, html) {
// Render Call - featuring an error handler for what happens if rendering fails
res.render(res.template, templateData, function renderResponse(err, html) {
if (!err) {
return res.send(html);
}

View File

@ -9,22 +9,23 @@ module.exports = function siteRouter() {
var router = express.Router(),
routeKeywords = config.get('routeKeywords');
// ### Admin routes
// Admin redirects - register redirect as route
// TODO: this should be middleware!
router.get(/^\/(logout|signout)\/$/, function (req, res) { return utils.url.redirectToAdmin(301, res, '#/signout/'); });
router.get(/^\/signup\/$/, function (req, res) { return utils.url.redirectToAdmin(301, res, '#/signup/'); });
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin|login)\/?)$/, function (req, res) { return utils.url.redirectToAdmin(301, res, '/'); });
// Post Live Preview
// Preview - register controller as route
router.get(utils.url.urlJoin('/', routeKeywords.preview, ':uuid', ':options?'), controllers.preview);
// Channels
// Channels - register sub-router
router.use(channels.router());
// setup routes for apps
// Apps - register sub-router
router.use(apps.router);
// Default
// Default - register single controller as route
router.get('*', controllers.single);
return router;

View File

@ -75,7 +75,7 @@ describe('AMP Controller', function () {
done();
};
ampController.controller(req, res, failTest(done));
ampController.renderer(req, res, failTest(done));
});
it('should render theme amp page when theme has amp template', function (done) {
@ -87,13 +87,13 @@ describe('AMP Controller', function () {
done();
};
ampController.controller(req, res, failTest(done));
ampController.renderer(req, res, failTest(done));
});
it('throws 404 when req.body has no post', function (done) {
req.body = {};
ampController.controller(req, res, function (err) {
ampController.renderer(req, res, function (err) {
should.exist(err);
should.exist(err.message);
should.exist(err.statusCode);
@ -112,7 +112,7 @@ describe('AMP Controller', function () {
}
};
ampController.controller(req, res, function (err) {
ampController.renderer(req, res, function (err) {
should.exist(err);
should.exist(err.message);
should.exist(err.statusCode);

View File

@ -4,7 +4,7 @@ var should = require('should'),
path = require('path'),
configUtils = require('../../../utils/configUtils'),
themes = require('../../../../server/themes'),
privateController = require('../../../../server/apps/private-blogging/lib/router').controller,
privateController = require('../../../../server/apps/private-blogging/lib/router'),
sandbox = sinon.sandbox.create();
@ -60,7 +60,7 @@ describe('Private Controller', function () {
done();
};
privateController(req, res, failTest(done));
privateController.renderer(req, res, failTest(done));
});
it('Should render theme password page when it exists', function (done) {
@ -72,7 +72,7 @@ describe('Private Controller', function () {
done();
};
privateController(req, res, failTest(done));
privateController.renderer(req, res, failTest(done));
});
it('Should render with error when error is passed in', function (done) {
@ -84,6 +84,6 @@ describe('Private Controller', function () {
done();
};
privateController(req, res, failTest(done));
privateController.renderer(req, res, failTest(done));
});
});