mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Dynamic Routing Beta: Added redirect middleware to ParentRouter
refs #9601 - middleware to control dominant router redirects
This commit is contained in:
parent
1952c55d33
commit
51e6286924
2 changed files with 275 additions and 0 deletions
|
@ -10,6 +10,7 @@ const debug = require('ghost-ignition').debug('services:routing:ParentRouter'),
|
|||
EventEmitter = require('events').EventEmitter,
|
||||
express = require('express'),
|
||||
_ = require('lodash'),
|
||||
url = require('url'),
|
||||
setPrototypeOf = require('setprototypeof'),
|
||||
security = require('../../lib/security'),
|
||||
urlService = require('../url'),
|
||||
|
@ -47,6 +48,49 @@ class ParentRouter extends EventEmitter {
|
|||
this._router = GhostRouter({mergeParams: true, parent: this});
|
||||
}
|
||||
|
||||
_getSiteRouter(req) {
|
||||
let siteRouter = null;
|
||||
|
||||
req.app._router.stack.every((router) => {
|
||||
if (router.name === 'SiteRouter') {
|
||||
siteRouter = router;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return siteRouter;
|
||||
}
|
||||
|
||||
_respectDominantRouter(req, res, next, slug) {
|
||||
let siteRouter = this._getSiteRouter(req);
|
||||
let targetRoute = null;
|
||||
|
||||
siteRouter.handle.stack.every((router) => {
|
||||
if (router.handle.parent && router.handle.parent.isRedirectEnabled && router.handle.parent.isRedirectEnabled(this.getType(), slug)) {
|
||||
targetRoute = router.handle.parent.getRoute();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (targetRoute) {
|
||||
debug('_respectDominantRouter');
|
||||
|
||||
const matchPath = this.permalinks.getValue().replace(':slug', '[a-zA-Z0-9-_]+');
|
||||
const toAppend = req.url.replace(new RegExp(matchPath), '');
|
||||
|
||||
return urlService.utils.redirect301(res, url.format({
|
||||
pathname: urlService.utils.createUrl(urlService.utils.urlJoin(targetRoute, toAppend), false, false, true),
|
||||
search: url.parse(req.originalUrl).search
|
||||
}));
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
mountRouter(path, router) {
|
||||
if (arguments.length === 1) {
|
||||
router = path;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
const should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
configUtils = require('../../../utils/configUtils'),
|
||||
settingsCache = require('../../../../server/services/settings/cache'),
|
||||
common = require('../../../../server/lib/common'),
|
||||
urlService = require('../../../../server/services/url'),
|
||||
ParentRouter = require('../../../../server/services/routing/ParentRouter'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
|
@ -14,7 +16,15 @@ describe('UNIT - services/routing/ParentRouter', function () {
|
|||
sandbox.stub(common.events, 'emit');
|
||||
sandbox.stub(common.events, 'on');
|
||||
|
||||
sandbox.stub(urlService.utils, 'redirect301');
|
||||
|
||||
req = sandbox.stub();
|
||||
req.app = {
|
||||
_router: {
|
||||
stack: []
|
||||
}
|
||||
};
|
||||
|
||||
res = sandbox.stub();
|
||||
next = sandbox.stub();
|
||||
|
||||
|
@ -23,7 +33,228 @@ describe('UNIT - services/routing/ParentRouter', function () {
|
|||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
describe('fn: _getSiteRouter', function () {
|
||||
it('find site router', function () {
|
||||
const parentRouter = new ParentRouter();
|
||||
|
||||
req.app = {
|
||||
_router: {
|
||||
stack: [{
|
||||
name: 'SiteRouter'
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
should.exist(parentRouter._getSiteRouter(req));
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn: _respectDominantRouter', function () {
|
||||
it('redirect', function () {
|
||||
const parentRouter = new ParentRouter();
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.url = '/tag/bacon/';
|
||||
req.originalUrl = '/tag/bacon/';
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(true),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(false);
|
||||
urlService.utils.redirect301.withArgs(res, '/channel/').calledOnce.should.be.true();
|
||||
});
|
||||
|
||||
it('redirect with query params', function () {
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.url = '/tag/bacon/';
|
||||
req.originalUrl = '/tag/bacon/?a=b';
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(true),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(false);
|
||||
urlService.utils.redirect301.withArgs(res, '/channel/?a=b').calledOnce.should.be.true();
|
||||
});
|
||||
|
||||
it('redirect rss', function () {
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.url = '/tag/bacon/rss/';
|
||||
req.originalUrl = '/tag/bacon/rss/';
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(true),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(false);
|
||||
urlService.utils.redirect301.withArgs(res, '/channel/rss/').calledOnce.should.be.true();
|
||||
});
|
||||
|
||||
it('redirect pagination', function () {
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.url = '/tag/bacon/page/2/';
|
||||
req.originalUrl = '/tag/bacon/page/2/';
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(true),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(false);
|
||||
urlService.utils.redirect301.withArgs(res, '/channel/page/2/').calledOnce.should.be.true();
|
||||
});
|
||||
|
||||
it('redirect correctly with subdirectory', function () {
|
||||
configUtils.set('url', 'http://localhost:7777/blog/');
|
||||
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.url = '/tag/bacon/';
|
||||
req.originalUrl = '/blog/tag/bacon/';
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(true),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(false);
|
||||
urlService.utils.redirect301.withArgs(res, '/blog/channel/').calledOnce.should.be.true();
|
||||
});
|
||||
|
||||
it('no redirect: different data key', function () {
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticRoutesRouter',
|
||||
handle: {
|
||||
parent: {
|
||||
isRedirectEnabled: sandbox.stub().returns(false),
|
||||
getRoute: sandbox.stub().returns('/channel/')
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(true);
|
||||
urlService.utils.redirect301.called.should.be.false();
|
||||
});
|
||||
|
||||
it('no redirect: no channel defined', function () {
|
||||
const parentRouter = new ParentRouter('tag', '/tag/:slug/');
|
||||
parentRouter.getType = sandbox.stub().returns('tags');
|
||||
parentRouter.permalinks = {
|
||||
getValue: sandbox.stub().returns('/tag/:slug/')
|
||||
};
|
||||
|
||||
req.app._router.stack = [{
|
||||
name: 'SiteRouter',
|
||||
handle: {
|
||||
stack: [{
|
||||
name: 'StaticPagesRouter',
|
||||
handle: {}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
parentRouter._respectDominantRouter(req, res, next, 'bacon');
|
||||
next.called.should.eql(true);
|
||||
urlService.utils.redirect301.called.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn: isRedirectEnabled', function () {
|
||||
it('no data key defined', function () {
|
||||
const parentRouter = new ParentRouter();
|
||||
|
|
Loading…
Add table
Reference in a new issue