mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 18:01:36 +03:00
042618fe93
refs 5a62253466
- The UrlService was relying on a "hidden" identifier field that was passed along with a router object. Now it's passed as an explicit parameter from the "frontend" to the backend's UrlService
193 lines
7.8 KiB
JavaScript
193 lines
7.8 KiB
JavaScript
const debug = require('@tryghost/debug')('routing');
|
|
const _ = require('lodash');
|
|
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 EmailRouter = require('./EmailRouter');
|
|
const UnsubscribeRouter = require('./UnsubscribeRouter');
|
|
|
|
// This emits its own routing events
|
|
const events = require('../../../server/lib/common/events');
|
|
|
|
class RouterManager {
|
|
constructor({registry, defaultApiVersion = 'v4'}) {
|
|
this.registry = registry;
|
|
this.defaultApiVersion = defaultApiVersion;
|
|
this.siteRouter = null;
|
|
this.urlService = null;
|
|
}
|
|
|
|
owns(routerId, id) {
|
|
return this.urlService.owns(routerId, id);
|
|
}
|
|
|
|
getUrlByResourceId(id, options) {
|
|
return this.urlService.getUrlByResourceId(id, options);
|
|
}
|
|
|
|
getResourceById(resourceId) {
|
|
return this.urlService.getResourceById(resourceId);
|
|
}
|
|
|
|
routerCreated(router) {
|
|
// NOTE: this event should be become an "internal frontend even"
|
|
// and should not be consumed by the modules outside the frontend
|
|
events.emit('router.created', this);
|
|
|
|
// CASE: there are router types which do not generate resource urls
|
|
// e.g. static route router, in this case we don't want ot notify the URL service
|
|
if (!router || !router.getPermalinks()) {
|
|
return;
|
|
}
|
|
|
|
this.urlService.onRouterAddedType(
|
|
router.identifier,
|
|
router.filter,
|
|
router.getResourceType(),
|
|
router.getPermalinks().getValue()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @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-initialize routing
|
|
*
|
|
* @param {Object} options
|
|
* @param {Boolean} [options.start] - flag controlling if the frontend Routes should be reinitialized
|
|
* @param {String} options.apiVersion - API version frontend Routes should communicate through
|
|
* @param {Object} options.routerSettings - JSON configuration to build frontend Routes
|
|
* @param {Object} options.urlService - service providing resource URL utility functions such as owns, getUrlByResourceId, and getResourceById
|
|
* @returns {ExpressRouter}
|
|
*/
|
|
init({start = false, routerSettings, apiVersion, urlService}) {
|
|
this.urlService = urlService;
|
|
debug('routing init', start, apiVersion, routerSettings);
|
|
|
|
this.registry.resetAllRouters();
|
|
this.registry.resetAllRoutes();
|
|
|
|
// NOTE: this event could become an "internal frontend" in the future, it's used has been kept to prevent
|
|
// from tying up this module with sitemaps
|
|
events.emit('routers.reset');
|
|
|
|
this.siteRouter = new ParentRouter('SiteRouter');
|
|
this.registry.setRouter('siteRouter', this.siteRouter);
|
|
|
|
if (start) {
|
|
apiVersion = apiVersion || this.defaultApiVersion;
|
|
this.start(apiVersion, routerSettings);
|
|
}
|
|
|
|
return this.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} routerSettings
|
|
*/
|
|
start(apiVersion, routerSettings) {
|
|
debug('routing start', apiVersion, routerSettings);
|
|
const RESOURCE_CONFIG = require(`./config/${apiVersion}`);
|
|
|
|
const unsubscribeRouter = new UnsubscribeRouter();
|
|
this.siteRouter.mountRouter(unsubscribeRouter.router());
|
|
this.registry.setRouter('unsubscribeRouter', unsubscribeRouter);
|
|
|
|
if (RESOURCE_CONFIG.QUERY.email) {
|
|
const emailRouter = new EmailRouter(RESOURCE_CONFIG);
|
|
this.siteRouter.mountRouter(emailRouter.router());
|
|
this.registry.setRouter('emailRouter', emailRouter);
|
|
}
|
|
|
|
const previewRouter = new PreviewRouter(RESOURCE_CONFIG);
|
|
this.siteRouter.mountRouter(previewRouter.router());
|
|
this.registry.setRouter('previewRouter', previewRouter);
|
|
|
|
_.each(routerSettings.routes, (value, key) => {
|
|
const staticRoutesRouter = new StaticRoutesRouter(key, value, this.routerCreated.bind(this));
|
|
this.siteRouter.mountRouter(staticRoutesRouter.router());
|
|
|
|
this.registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
|
|
});
|
|
|
|
_.each(routerSettings.collections, (value, key) => {
|
|
const collectionRouter = new CollectionRouter(key, value, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
this.siteRouter.mountRouter(collectionRouter.router());
|
|
this.registry.setRouter(collectionRouter.identifier, collectionRouter);
|
|
});
|
|
|
|
const staticPagesRouter = new StaticPagesRouter(RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
this.siteRouter.mountRouter(staticPagesRouter.router());
|
|
|
|
this.registry.setRouter('staticPagesRouter', staticPagesRouter);
|
|
|
|
_.each(routerSettings.taxonomies, (value, key) => {
|
|
const taxonomyRouter = new TaxonomyRouter(key, value, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
this.siteRouter.mountRouter(taxonomyRouter.router());
|
|
|
|
this.registry.setRouter(taxonomyRouter.identifier, taxonomyRouter);
|
|
});
|
|
|
|
const appRouter = new ParentRouter('AppsRouter');
|
|
this.siteRouter.mountRouter(appRouter.router());
|
|
|
|
this.registry.setRouter('appRouter', appRouter);
|
|
|
|
debug('Routes:', this.registry.getAllRoutes());
|
|
}
|
|
|
|
/**
|
|
* This is a glue code to keep the implementation of routers away from
|
|
* this sort of logic. Ideally this method should not be ever called
|
|
* and handled completely on the URL Service layer without touching the frontend
|
|
* @param {Object} settingModel instance of the settings model
|
|
* @returns {void}
|
|
*/
|
|
handleTimezoneEdit(settingModel) {
|
|
const newTimezone = settingModel.attributes.value;
|
|
const previousTimezone = settingModel._previousAttributes.value;
|
|
|
|
if (newTimezone === previousTimezone) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* CASE: timezone changes
|
|
*
|
|
* If your permalink contains a date reference, we have to regenerate the urls.
|
|
*
|
|
* e.g. /:year/:month/:day/:slug/ or /:day/:slug/
|
|
*/
|
|
|
|
// NOTE: timezone change only affects the collection router with dated permalinks
|
|
const collectionRouter = this.registry.getRouterByName('CollectionRouter');
|
|
if (collectionRouter.getPermalinks().getValue().match(/:year|:month|:day/)) {
|
|
debug('handleTimezoneEdit: trigger regeneration');
|
|
|
|
this.urlService.onRouterUpdated(collectionRouter.identifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = RouterManager;
|