feat: Graceful frontend extension initialization failure ()

This commit is contained in:
Sami Mazouz 2022-04-03 22:17:57 +01:00 committed by GitHub
parent 4dfbaa3271
commit ca7055f5d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 2 deletions
framework/core

@ -33,6 +33,7 @@ import type Mithril from 'mithril';
import type Component from './Component';
import type { ComponentAttrs } from './Component';
import Model, { SavedModelData } from './Model';
import fireApplicationError from './helpers/fireApplicationError';
export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
@ -262,7 +263,25 @@ export default class Application {
}
public boot() {
this.initializers.toArray().forEach((initializer) => initializer(this));
const caughtInitializationErrors: CallableFunction[] = [];
this.initializers.toArray().forEach((initializer) => {
try {
initializer(this);
} catch (e) {
const extension = initializer.itemName.includes('/')
? initializer.itemName.replace(/(\/flarum-ext-)|(\/flarum-)/g, '-')
: initializer.itemName;
caughtInitializationErrors.push(() =>
fireApplicationError(
extractText(app.translator.trans('core.lib.error.extension_initialiation_failed_message', { extension })),
`${extension} failed to initialize`,
e
)
);
}
});
this.store.pushPayload({ data: this.data.resources });
@ -273,6 +292,8 @@ export default class Application {
this.mount();
this.initialRoute = window.location.href;
caughtInitializationErrors.forEach((handler) => handler());
}
// TODO: This entire system needs a do-over for v2

@ -0,0 +1,18 @@
import app from '../app';
/**
* Fire a Flarum error which is shown in the JS console for everyone and in an alert for the admin.
*
* @param userTitle: a user friendly title of the error, should be localized.
* @param consoleTitle: an error title that goes in the console, doesn't have to be localized.
* @param error: the error.
*/
export default function fireApplicationError(userTitle: string, consoleTitle: string, error: any) {
console.group(`%c${consoleTitle}`, 'background-color: #d83e3e; color: #ffffff; font-weight: bold;');
console.error(error);
console.groupEnd();
if (app.session?.user?.isAdmin()) {
app.alerts.show({ type: 'error' }, `${userTitle}`);
}
}

@ -42,6 +42,10 @@ export default class User extends Model {
return Model.hasMany<Group>('groups').call(this);
}
isAdmin() {
return Model.attribute<boolean | undefined>('isAdmin').call(this);
}
joinTime() {
return Model.attribute('joinTime', Model.transformDate).call(this);
}

@ -534,6 +534,7 @@ core:
# These translations are displayed as error messages.
error:
dependent_extensions_message: "Cannot disable {extension} until the following dependent extensions are disabled: {extensions}"
extension_initialiation_failed_message: "{extension} failed to initialize, check the browser console for further information."
generic_message: "Oops! Something went wrong. Please reload the page and try again."
missing_dependencies_message: "Cannot enable {extension} until the following dependencies are enabled: {extensions}"
not_found_message: The requested resource was not found.

@ -25,7 +25,8 @@ class CurrentUserSerializer extends UserSerializer
'markedAllAsReadAt' => $this->formatDate($user->marked_all_as_read_at),
'unreadNotificationCount' => (int) $user->getUnreadNotificationCount(),
'newNotificationCount' => (int) $user->getNewNotificationCount(),
'preferences' => (array) $user->preferences
'preferences' => (array) $user->preferences,
'isAdmin' => $user->isAdmin(),
];
return $attributes;