Plugin with catch-all wildcard route map

Discourse has a catch-all route using a wildcard path.

app/assets/javascripts/discourse/mapping-router.js.es6:

this.route("unknown", { path: "*path" });

As far as I can tell, this stops plugins from having the same catch-all rule because Discourse’s rule is executed later, thus taking precedence. Atleast when commenting out Discourse’s rule temporarily, the following works as expected:

plugins/plugin/assets/javascripts/discourse/plugin-name-route-map.js.es6:

this.route('main-route', { path: '*wildcard' });

Is there a way to have a catch-all route for a plugin with only one route?


Attempt 1:

Nested routes.

plugins/plugin/assets/javascripts/discourse/plugin-name-route-map.js.es6:

export default function () {
  // Matches single-segment paths.
  this.route('main-route', { path: '/:path' }, function () {
    // Optionally, matches subsequent segments.
    this.route('sub-route', { path: '*wildcard_path' });
  });
}

This has the major issue that requests matching /:path will use the route main-route.index whereas requests matching /:path/*wildcard will use the route main-route.sub-route. I need all requests to go through main-route.index.


Attempt 2:

Multiple paths, same routes.

(This seems to have worked in older Ember version, but does no longer.)

plugins/plugin/assets/javascripts/discourse/plugin-name-route-map.js.es6:

export default function () {
  // Matches single-segment paths.
  this.route('main-route', { path: '/:path' });

  // Doesn’t match multi-segment paths.
  this.route('main-route', { path: '/:path/*wildcard_path' });
}

or

plugins/plugin/assets/javascripts/discourse/plugin-name-route-map.js.es6:

export default function () {
  // Matches multi-segment paths.
  this.route('main-route', { path: '/:path/*wildcard_path' });

  // Doesn’t match single-segment paths.
  this.route('main-route', { path: '/:path' });
}
1 Like

I am not sure about this, but maybe @eviltrout can have a quick look.

1 Like

I don’t think there’s a way to do this. Is there a reason you can’t define all the routes you’re using? I don’t see the downside.

The paths I want to direct to the same route are not known before. They’re created at run time.

Can you not use a single route and put the stuff that you want in the route into the load?

Technically, all routes are defined at run time (that’s how a route map works) but I assume you mean after the app has loaded?

Could you not use a dynamic segment for your case? If not, could you be more specific about what you are trying to achieve?

I’m working on a plugin which works as a kind of reverse proxy. It allows users to specify routes which upon requesting are forwarded to another URL. The goal is to allow arbitrary web applications to be served within a Discourse instance.

For this, I essentially need to handle all routes that were defined by the plugin users. Therefor, I thought, it’s necessary to catch all requests that aren’t handled by Discourse. In a nutshell, I have unknown paths (they might have multiple segments, query parameters, etc.) that I all want to use the same route for. The route then makes an AJAX call in order to forward the request and retrieve the web application’s content.

I do realize that this is a complex plugin and use case.


So far, I wasn’t able to make this work with any combination of dynamic path segments and wildcard path segments I tried.

What you are suggesting is quite complicated unfortunately. I suspect you will have to play with Ember’s routing internals to make it work.

3 Likes

(via Override existing catch-all wildcard route - Routing - Ember.JS)

The following allows a plugin to have a sort of catch-all style route:

plugins/plugin/assets/javascripts/discourse/plugin-name-route-map.js.es6:

export default function () {
  this.route('main-route.single-segment', { path: '/:path' });
  this.route('main-route.multi-segment', { path: '/:path/*wildcard' });
}

plugins/plugin/assets/javascripts/discourse/routes/main-route-single-segment.js.es6:

export { default } from './main-route';

plugins/plugin/assets/javascripts/discourse/routes/main-route-multi-segment.js.es6:

export { default } from './main-route';

The main route is the one with an actual implementation. Also note that you need to export any controllers/templates in the same fashion if you want to duplicate their behavior.

2 Likes

Is there any chance Discourse could provide plugins the ability to accomplish this without a workaround?

Maybe you could use a permalink redirect.