mirror of
https://github.com/discourse/discourse.git
synced 2025-04-02 00:55:59 +08:00
DEV: Show theme/plugin error banner for route loading failures (#24218)
This aims to help admins and developers identify the cause of loading issues on routes. As with other theme/plugin errors, the UI banner is only shown to administrators. For non-admins, the information is only written to the browser console.
This commit is contained in:
parent
08e2ee3ec1
commit
67bcef3959
@ -8,6 +8,7 @@ import { setting } from "discourse/lib/computed";
|
|||||||
import cookie from "discourse/lib/cookie";
|
import cookie from "discourse/lib/cookie";
|
||||||
import logout from "discourse/lib/logout";
|
import logout from "discourse/lib/logout";
|
||||||
import mobile from "discourse/lib/mobile";
|
import mobile from "discourse/lib/mobile";
|
||||||
|
import identifySource, { consolePrefix } from "discourse/lib/source-identifier";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import Category from "discourse/models/category";
|
import Category from "discourse/models/category";
|
||||||
import Composer from "discourse/models/composer";
|
import Composer from "discourse/models/composer";
|
||||||
@ -39,6 +40,7 @@ const ApplicationRoute = DiscourseRoute.extend({
|
|||||||
loadingSlider: service(),
|
loadingSlider: service(),
|
||||||
router: service(),
|
router: service(),
|
||||||
siteSettings: service(),
|
siteSettings: service(),
|
||||||
|
clientErrorHandler: service(),
|
||||||
|
|
||||||
get includeExternalLoginMethods() {
|
get includeExternalLoginMethods() {
|
||||||
return (
|
return (
|
||||||
@ -117,15 +119,24 @@ const ApplicationRoute = DiscourseRoute.extend({
|
|||||||
const xhrOrErr = err.jqXHR ? err.jqXHR : err;
|
const xhrOrErr = err.jqXHR ? err.jqXHR : err;
|
||||||
const exceptionController = this.controllerFor("exception");
|
const exceptionController = this.controllerFor("exception");
|
||||||
|
|
||||||
const c = window.console;
|
const themeOrPluginSource = identifySource(err);
|
||||||
if (c && c.error) {
|
|
||||||
c.error(xhrOrErr);
|
// eslint-disable-next-line no-console
|
||||||
}
|
console.error(
|
||||||
|
...[consolePrefix(err, themeOrPluginSource), xhrOrErr].filter(Boolean)
|
||||||
|
);
|
||||||
|
|
||||||
if (xhrOrErr && xhrOrErr.status === 404) {
|
if (xhrOrErr && xhrOrErr.status === 404) {
|
||||||
return this.router.transitionTo("exception-unknown");
|
return this.router.transitionTo("exception-unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (themeOrPluginSource) {
|
||||||
|
this.clientErrorHandler.displayErrorNotice(
|
||||||
|
"Error loading route",
|
||||||
|
themeOrPluginSource
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
exceptionController.setProperties({
|
exceptionController.setProperties({
|
||||||
lastTransition: transition,
|
lastTransition: transition,
|
||||||
thrown: xhrOrErr,
|
thrown: xhrOrErr,
|
||||||
|
@ -83,11 +83,15 @@ export default class ClientErrorHandlerService extends Service {
|
|||||||
|
|
||||||
let html = `⚠️ ${escape(message)}`;
|
let html = `⚠️ ${escape(message)}`;
|
||||||
|
|
||||||
if (source && source.type === "theme") {
|
if (source?.type === "theme") {
|
||||||
html += `<br/>${I18n.t("themes.error_caused_by", {
|
html += `<br/>${I18n.t("themes.error_caused_by", {
|
||||||
name: escape(source.name),
|
name: escape(source.name),
|
||||||
path: source.path,
|
path: source.path,
|
||||||
})}`;
|
})}`;
|
||||||
|
} else if (source?.type === "plugin") {
|
||||||
|
html += `<br/>${I18n.t("broken_plugin_alert", {
|
||||||
|
name: escape(source.name),
|
||||||
|
})}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
html += `<br/><span class='theme-error-suffix'>${I18n.t(
|
html += `<br/><span class='theme-error-suffix'>${I18n.t(
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
import { getOwner } from "@ember/application";
|
||||||
|
import { visit } from "@ember/test-helpers";
|
||||||
|
import { test } from "qunit";
|
||||||
|
import Sinon from "sinon";
|
||||||
|
import { acceptance } from "../helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("client-error-handler service", function (needs) {
|
||||||
|
needs.user({
|
||||||
|
admin: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays route-loading errors caused by themes", async function (assert) {
|
||||||
|
const fakeError = new Error("Something bad happened");
|
||||||
|
fakeError.stack = "assets/plugins/some-fake-plugin-name.js";
|
||||||
|
|
||||||
|
const topicRoute = getOwner(this).lookup("route:topic");
|
||||||
|
Sinon.stub(topicRoute, "model").throws(fakeError);
|
||||||
|
|
||||||
|
const consoleStub = Sinon.stub(console, "error");
|
||||||
|
try {
|
||||||
|
await visit("/t/280");
|
||||||
|
} catch {}
|
||||||
|
consoleStub.restore();
|
||||||
|
|
||||||
|
assert.dom(".broken-theme-alert-banner").exists();
|
||||||
|
assert
|
||||||
|
.dom(".broken-theme-alert-banner")
|
||||||
|
.containsText("some-fake-plugin-name");
|
||||||
|
});
|
||||||
|
});
|
@ -221,6 +221,8 @@ en:
|
|||||||
|
|
||||||
broken_decorator_alert: "Posts may not display correctly because one of the post content decorators on your site raised an error."
|
broken_decorator_alert: "Posts may not display correctly because one of the post content decorators on your site raised an error."
|
||||||
|
|
||||||
|
broken_plugin_alert: "Caused by plugin '%{name}'"
|
||||||
|
|
||||||
s3:
|
s3:
|
||||||
regions:
|
regions:
|
||||||
ap_northeast_1: "Asia Pacific (Tokyo)"
|
ap_northeast_1: "Asia Pacific (Tokyo)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user