🐛 Fixed frontend routing prioritizing collections over built in routes (#20765)

ref
https://linear.app/tryghost/issue/ONC-242/frontend-routing-prioritizes-collections-over-taxonomies

- Under a fairly specific edge case with a collection route that conflicts with a default, built-in route ("taxonomy" — like tags, authors, etc), the frontend routing would prioritize the collection over the taxonomy.

- For example, with the following in a custom `routes.yaml`:
```
collections:
  /:
    permalink: /{primary_tag}/{slug}/
    template: index
```

If a post exists with the same slug as its primary tag's slug, the frontend routing would redirect the `/tag/{slug}/` route to the post in the collection, rather than serving the tag itself.

- This commit changes that, so if a collection's route conflicts with e.g. a `/tag/{slug}/` default route, Ghost will still return the built in route, rather than the collection.
This commit is contained in:
Chris Raible 2024-08-21 13:45:59 -07:00 committed by GitHub
parent f08e4d4728
commit c0471f0c28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 57 additions and 7 deletions

View File

@ -126,6 +126,13 @@ class RouterManager {
this.registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
});
_.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);
});
_.each(routerSettings.collections, (value, key) => {
const collectionRouter = new CollectionRouter(key, value, RESOURCE_CONFIG, this.routerCreated.bind(this));
this.siteRouter.mountRouter(collectionRouter.router());
@ -137,13 +144,6 @@ class RouterManager {
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());

View File

@ -80,3 +80,36 @@ describe('Custom Frontend routing', function () {
.expect(assertCorrectFrontendHeaders);
});
});
describe('Custom frontend routing - edge cases', function () {
let request;
before(async function () {
const routesFilePath = path.join(configUtils.config.get('paths:appRoot'), 'test/utils/fixtures/settings/edgecaseroutes.yaml');
await configUtils.restore();
await testUtils.stopGhost();
await testUtils.startGhost({
forceStart: true,
routesFilePath,
subdir: false
});
request = supertest.agent(configUtils.config.get('url'));
});
after(async function () {
await testUtils.stopGhost();
});
it('should prioritize taxonomies over collections', async function () {
// Create a tag and a post with the same slug
const slug = 'cheese';
await testUtils.createTag({tag: {slug}});
await testUtils.createPost({post: {tags: [{slug}], slug}});
// Test that the tag route takes precedence
await request.get(`/tag/${slug}/`)
.expect(200);
});
});

View File

@ -0,0 +1,10 @@
routes:
collections:
/:
permalink: /{primary_tag}/{slug}/
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/

View File

@ -79,6 +79,12 @@ const createPost = function createPost(options) {
return models.Post.add(post, context.internal);
};
const createTag = function createTag(options) {
const tag = DataGenerator.forKnex.createTag(options.tag);
return models.Tag.add(tag, context.internal);
};
const createEmail = function createEmail(options) {
const email = DataGenerator.forKnex.createEmail(options.email);
return models.Email.add(email, context.internal);
@ -103,6 +109,7 @@ module.exports = {
setup: setup,
createUser: createUser,
createPost: createPost,
createTag: createTag,
createEmailedPost,
/**