const debug = require('@tryghost/debug')('services:routing:bootstrap'); const _ = require('lodash'); const events = require('../../../server/lib/common/events'); const frontendSettings = require('../settings'); const StaticRoutesRouter = require('./StaticRoutesRouter'); const StaticPagesRouter = require('./StaticPagesRouter'); const CollectionRouter = require('./CollectionRouter'); const TaxonomyRouter = require('./TaxonomyRouter'); const PreviewRouter = require('./PreviewRouter'); const ParentRouter = require('./ParentRouter'); const UnsubscribeRouter = require('./UnsubscribeRouter'); const defaultApiVersion = 'v4'; const registry = require('./registry'); let siteRouter; /** * @description The `init` function will return the wrapped parent express router and will start creating all * routers if you pass the option "start: true". * * CASES: * - if Ghost starts, it will first init the site app with the wrapper router and then call `start` * separately, because it could be that your blog goes into maintenance mode * - if you change your route settings, we will re-initialise routing * * @param {Object} options * @returns {ExpressRouter} */ module.exports.init = (options = {start: false}) => { debug('bootstrap'); registry.resetAllRouters(); registry.resetAllRoutes(); events.emit('routers.reset'); siteRouter = new ParentRouter('SiteRouter'); registry.setRouter('siteRouter', siteRouter); if (options.start) { let apiVersion = _.isBoolean(options.start) ? defaultApiVersion : options.start; // NOTE: Get the routes.yaml config const dynamicRoutes = frontendSettings.get('routes'); this.start(apiVersion, dynamicRoutes); } return siteRouter.router(); }; /** * @description This function will create the routers based on the route settings * * The routers are created in a specific order. This order defines who can get a resource first or * who can dominant other routers. * * 1. Preview + Unsubscribe Routers: Strongest inbuilt features, which you can never override. * 2. Static Routes: Very strong, because you can override any urls and redirect to a static route. * 3. Taxonomies: Stronger than collections, because it's an inbuilt feature. * 4. Collections * 5. Static Pages: Weaker than collections, because we first try to find a post slug and fallback to lookup a static page. * 6. Internal Apps: Weakest * * @param {string} apiVersion * @param {object} dynamicRoutes */ module.exports.start = (apiVersion, dynamicRoutes) => { const RESOURCE_CONFIG = require(`./config/${apiVersion}`); const unsubscribeRouter = new UnsubscribeRouter(); siteRouter.mountRouter(unsubscribeRouter.router()); registry.setRouter('unsubscribeRouter', unsubscribeRouter); const previewRouter = new PreviewRouter(RESOURCE_CONFIG); siteRouter.mountRouter(previewRouter.router()); registry.setRouter('previewRouter', previewRouter); _.each(dynamicRoutes.routes, (value, key) => { const staticRoutesRouter = new StaticRoutesRouter(key, value); siteRouter.mountRouter(staticRoutesRouter.router()); registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter); }); _.each(dynamicRoutes.collections, (value, key) => { const collectionRouter = new CollectionRouter(key, value, RESOURCE_CONFIG); siteRouter.mountRouter(collectionRouter.router()); registry.setRouter(collectionRouter.identifier, collectionRouter); }); const staticPagesRouter = new StaticPagesRouter(RESOURCE_CONFIG); siteRouter.mountRouter(staticPagesRouter.router()); registry.setRouter('staticPagesRouter', staticPagesRouter); _.each(dynamicRoutes.taxonomies, (value, key) => { const taxonomyRouter = new TaxonomyRouter(key, value, RESOURCE_CONFIG); siteRouter.mountRouter(taxonomyRouter.router()); registry.setRouter(taxonomyRouter.identifier, taxonomyRouter); }); const appRouter = new ParentRouter('AppsRouter'); siteRouter.mountRouter(appRouter.router()); registry.setRouter('appRouter', appRouter); debug('Routes:', registry.getAllRoutes()); };