diff --git a/core/server/services/apps/proxy.js b/core/server/services/apps/proxy.js index c502490813..96422d6074 100644 --- a/core/server/services/apps/proxy.js +++ b/core/server/services/apps/proxy.js @@ -73,7 +73,7 @@ generateProxyFunctions = function (name, permissions, isInternal) { // Expose the route service... routeService: { // This allows for mounting an entirely new Router at a path... - registerRouter: checkRegisterPermissions('routes', router.registerRouter.bind(router)) + registerRouter: checkRegisterPermissions('routes', router.mountRouter.bind(router)) }, // Mini proxy to the API - needs review api: { diff --git a/core/server/services/route/AppRouter.js b/core/server/services/route/AppRouter.js deleted file mode 100644 index d2da86e55b..0000000000 --- a/core/server/services/route/AppRouter.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; -/** - * An instance of router that is provided to Apps, to mount routes into. - */ - -var debug = require('ghost-ignition').debug('services:routes:app'), - Router = require('./base/Router'); - -class AppRouter extends Router { - registerRouter(path, router) { - debug('registerRouter for', path); - this.router.use(path, router); - } -} - -module.exports = AppRouter; diff --git a/core/server/services/route/app-router.js b/core/server/services/route/app-router.js new file mode 100644 index 0000000000..d8c5456b49 --- /dev/null +++ b/core/server/services/route/app-router.js @@ -0,0 +1,4 @@ +var Router = require('./base/Router'), + appRouter = new Router('apps'); + +module.exports = appRouter; diff --git a/core/server/services/route/base/Router.js b/core/server/services/route/base/Router.js index f167e76e40..756dc36d26 100644 --- a/core/server/services/route/base/Router.js +++ b/core/server/services/route/base/Router.js @@ -1,10 +1,48 @@ 'use strict'; +/** + * # Router + * + * A wrapper around express.Router + * Intended to be extended anywhere that routes need to be registered in Ghost + * Only allows for .use and .get at the moment - we don't have clear use-cases for anything else yet. + */ -var expressRouter = require('express').Router; - +var debug = require('ghost-ignition').debug('services:routes:ParentRouter'), + express = require('express'), + // This is a shared global cache + // @TODO expand this as part of the route service + routes = []; +/** + * We expose a very limited amount of express.Router via specialist methods + */ class Router { - constructor() { - this.router = expressRouter({mergeParams: true}); + constructor(name) { + this.name = name; + this._router = express.Router({mergeParams: true}); + } + + mountRouter(path, router) { + if (arguments.length === 1) { + router = path; + debug(this.name + ': mountRouter: ' + router.name); + this._router.use(router); + } else { + routes.push(path); + debug(this.name + ': mountRouter: ' + router.name + ' at ' + path); + this._router.use(path, router); + } + } + + mountRoute(path, controller) { + debug(this.name + ': mountRoute for', path, controller.name); + routes.push(path); + this._router.get(path, controller); + } + + router() { + // @TODO: should this just be the handler that is returned? + // return this._router.handle.bind(this._router); + return this._router; } } diff --git a/core/server/services/route/index.js b/core/server/services/route/index.js index 493429d575..41900dbd23 100644 --- a/core/server/services/route/index.js +++ b/core/server/services/route/index.js @@ -1,9 +1,23 @@ /** * # Route Service * - * Everything in here is 100% experimental + * Note: routes are patterns, not individual URLs, which have either + * subrouters, or controllers mounted on them. There are not that many routes. + * + * The route service is intended to: + * - handle the mounting of all the routes throughout the bootup sequence + * - keep track of the registered routes, and what they have mounted on them + * - provide a way for apps to register routes + * - keep routes being served in a sane order + * + * The route service does not handle: + * - redirects + * - assets + * These both happen prior to the routeService router being mounted */ -var AppRouter = require('./AppRouter'); +// We expose this via the App Proxy, so that Apps can register routes +module.exports.appRouter = require('./app-router'); +// This is the main router, that gets mounted in the express app in /site +module.exports.router = require('./site-router'); -module.exports.appRouter = new AppRouter(); diff --git a/core/server/services/route/site-router.js b/core/server/services/route/site-router.js new file mode 100644 index 0000000000..7cb124652e --- /dev/null +++ b/core/server/services/route/site-router.js @@ -0,0 +1,48 @@ +var Router = require('./base/Router'), + siteRouter = new Router('site'), + + // Sub Routers + appRouter = require('./app-router'), + channelService = require('../channels'), + + // Controllers + controllers = require('../../controllers'), + + // Utils for creating paths + // @TODO: refactor these away + config = require('../../config'), + utils = require('../../utils'), + + _private = {}; + +_private.mountDefaultRoutes = function mountDefaultRoutes() { + // @TODO move this path out of this file! + // Note this also exists in api/index.js + var previewRoute = utils.url.urlJoin('/', config.get('routeKeywords').preview, ':uuid', ':options?'); + + // Preview - register controller as route + // Ideal version, as we don't want these paths all over the place + // previewRoute = new Route('GET /:t_preview/:uuid/:options?', previewController); + // siteRouter.mountRoute(previewRoute); + // Orrrrr maybe preview should be an internal App??! + siteRouter.mountRoute(previewRoute, controllers.preview); + + // Channels - register sub-router + // The purpose of having a parentRouter for channels, is so that we can load channels from wherever we want: + // config, settings, apps, etc, and that it will be possible for the router to be reloaded. + siteRouter.mountRouter(channelService.router()); + + // Apps - register sub-router + // The purpose of having a parentRouter for apps, is that Apps can register a route whenever they want. + // Apps cannot yet deregister, it's complex to implement and I don't yet have a clear use-case for this. + siteRouter.mountRouter(appRouter.router()); + + // Default - register entry controller as route + siteRouter.mountRoute('*', controllers.entry); +}; + +module.exports = function router() { + _private.mountDefaultRoutes(); + + return siteRouter.router(); +}; diff --git a/core/server/site/app.js b/core/server/site/app.js index 0af271cdbc..78bd137596 100644 --- a/core/server/site/app.js +++ b/core/server/site/app.js @@ -10,8 +10,8 @@ var debug = require('ghost-ignition').debug('blog'), // This should probably be an internal app sitemapHandler = require('../data/xml/sitemap/handler'), - // routes - routes = require('./routes'), + // Route Service + routeService = require('../services/route'), // Global/shared middleware cacheControl = require('../middleware/cache-control'), @@ -122,7 +122,7 @@ module.exports = function setupSiteApp() { debug('General middleware done'); // Set up Frontend routes (including private blogging routes) - siteApp.use(routes()); + siteApp.use(routeService.router()); // ### Error handlers siteApp.use(errorHandler.pageNotFound); diff --git a/core/server/site/routes.js b/core/server/site/routes.js deleted file mode 100644 index c3591839a8..0000000000 --- a/core/server/site/routes.js +++ /dev/null @@ -1,25 +0,0 @@ -var express = require('express'), - config = require('../config'), - controllers = require('../controllers'), - channelService = require('../services/channels/'), - appRouter = require('../services/route').appRouter, - utils = require('../utils'); - -module.exports = function siteRouter() { - var router = express.Router(), - routeKeywords = config.get('routeKeywords'); - - // Preview - register controller as route - router.get(utils.url.urlJoin('/', routeKeywords.preview, ':uuid', ':options?'), controllers.preview); - - // Channels - register sub-router - router.use(channelService.router()); - - // Apps - register sub-router - router.use(appRouter.router); - - // Default - register entry controller as route - router.get('*', controllers.entry); - - return router; -};