0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Wrapped express router & expose from route service (#9206)

refs #9192

- Moving towards a centralised concept of routing / routes
- The base router now wraps express router, and offers us the features we need
- Site Router is the parent router, it gets initialised with all of our default routing
- App Router is a sub router for apps - apps register their routes/routers onto it.
- TODO: refactor channels subrouter to work this same way
- MAYBE: move the app router to the apps service
This commit is contained in:
Hannah Wolfe 2017-11-09 10:08:11 +00:00 committed by GitHub
parent 90cfdbe7a6
commit 7656d0bdda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 115 additions and 52 deletions

View file

@ -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: {

View file

@ -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;

View file

@ -0,0 +1,4 @@
var Router = require('./base/Router'),
appRouter = new Router('apps');
module.exports = appRouter;

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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();
};

View file

@ -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);

View file

@ -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;
};