+ // ###################### Getters ######################
+
+ isRegistered (npmName: string) {
+ return !!this.getRegisteredPluginOrTheme(npmName)
+ }
+
+ getRegisteredPluginOrTheme (npmName: string) {
+ return this.registeredPlugins[npmName]
+ }
+
+ getRegisteredPluginByShortName (name: string) {
+ const npmName = PluginModel.buildNpmName(name, PluginType.PLUGIN)
+ const registered = this.getRegisteredPluginOrTheme(npmName)
+
+ if (!registered || registered.type !== PluginType.PLUGIN) return undefined
+
+ return registered
+ }
+
+ getRegisteredThemeByShortName (name: string) {
+ const npmName = PluginModel.buildNpmName(name, PluginType.THEME)
+ const registered = this.getRegisteredPluginOrTheme(npmName)
+
+ if (!registered || registered.type !== PluginType.THEME) return undefined
+
+ return registered
+ }
+
+ getRegisteredPlugins () {
+ return this.getRegisteredPluginsOrThemes(PluginType.PLUGIN)
+ }
+
+ getRegisteredThemes () {
+ return this.getRegisteredPluginsOrThemes(PluginType.THEME)
+ }
+
+ getIdAndPassAuths () {
+ return this.getRegisteredPlugins()
+ .map(p => ({
+ npmName: p.npmName,
+ name: p.name,
+ version: p.version,
+ idAndPassAuths: p.registerHelpers.getIdAndPassAuths()
+ }))
+ .filter(v => v.idAndPassAuths.length !== 0)
+ }
+
+ getExternalAuths () {
+ return this.getRegisteredPlugins()
+ .map(p => ({
+ npmName: p.npmName,
+ name: p.name,
+ version: p.version,
+ externalAuths: p.registerHelpers.getExternalAuths()
+ }))
+ .filter(v => v.externalAuths.length !== 0)
+ }
+
+ getRegisteredSettings (npmName: string) {
+ const result = this.getRegisteredPluginOrTheme(npmName)
+ if (!result || result.type !== PluginType.PLUGIN) return []
+
+ return result.registerHelpers.getSettings()
+ }
+
+ getRouter (npmName: string) {
+ const result = this.getRegisteredPluginOrTheme(npmName)
+ if (!result || result.type !== PluginType.PLUGIN) return null
+
+ return result.registerHelpers.getRouter()
+ }
+
+ getTranslations (locale: string) {
+ return this.translations[locale] || {}
+ }
+
+ async isTokenValid (token: MOAuthTokenUser, type: 'access' | 'refresh') {
+ const auth = this.getAuth(token.User.pluginAuth, token.authName)
+ if (!auth) return true
+
+ if (auth.hookTokenValidity) {
+ try {
+ const { valid } = await auth.hookTokenValidity({ token, type })
+
+ if (valid === false) {
+ logger.info('Rejecting %s token validity from auth %s of plugin %s', type, token.authName, token.User.pluginAuth)
+ }
+
+ return valid
+ } catch (err) {
+ logger.warn('Cannot run check token validity from auth %s of plugin %s.', token.authName, token.User.pluginAuth, { err })
+ return true
+ }
+ }
+
+ return true
+ }
+
+ // ###################### External events ######################
+
+ async onLogout (npmName: string, authName: string, user: MUser, req: express.Request) {
+ const auth = this.getAuth(npmName, authName)
+
+ if (auth?.onLogout) {
+ logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName)
+
+ try {
+ // Force await, in case or onLogout returns a promise
+ const result = await auth.onLogout(user, req)
+
+ return typeof result === 'string'
+ ? result
+ : undefined
+ } catch (err) {
+ logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err })
+ }
+ }
+
+ return undefined
+ }
+
+ async onSettingsChanged (name: string, settings: any) {
+ const registered = this.getRegisteredPluginByShortName(name)
+ if (!registered) {
+ logger.error('Cannot find plugin %s to call on settings changed.', name)
+ }
+
+ for (const cb of registered.registerHelpers.getOnSettingsChangedCallbacks()) {
+ try {
+ await cb(settings)
+ } catch (err) {
+ logger.error('Cannot run on settings changed callback for %s.', registered.npmName, { err })
+ }
+ }
+ }
+
+ // ###################### Hooks ######################
+
+ async runHook<T> (hookName: ServerHookName, result?: T, params?: any): Promise<T> {
+ if (!this.hooks[hookName]) return Promise.resolve(result)
+
+ const hookType = getHookType(hookName)
+
+ for (const hook of this.hooks[hookName]) {
+ logger.debug('Running hook %s of plugin %s.', hookName, hook.npmName)
+
+ result = await internalRunHook(hook.handler, hookType, result, params, err => {
+ logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
+ })
+ }
+
+ return result
+ }
+
+ // ###################### Registration ######################
+
+ async registerPluginsAndThemes () {