From 5e2b2e2775421cd98286d6e2f75cf38aae7a212c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 10 Apr 2020 15:07:54 +0200 Subject: Add ability for plugins to add custom routes --- server/lib/plugins/plugin-manager.ts | 51 +++--- server/lib/plugins/register-helpers-store.ts | 235 +++++++++++++++++++++++++++ server/lib/plugins/register-helpers.ts | 180 -------------------- 3 files changed, 260 insertions(+), 206 deletions(-) create mode 100644 server/lib/plugins/register-helpers-store.ts delete mode 100644 server/lib/plugins/register-helpers.ts (limited to 'server/lib/plugins') diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index 44530d203..37fb07716 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts @@ -13,15 +13,14 @@ import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants' import { PluginType } from '../../../shared/models/plugins/plugin.type' import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' import { outputFile, readJSON } from 'fs-extra' -import { ServerHook, ServerHookName, serverHookObject } from '../../../shared/models/plugins/server-hook.model' +import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server-hook.model' import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' import { RegisterServerOptions } from '../../typings/plugins/register-server-option.model' import { PluginLibrary } from '../../typings/plugins' import { ClientHtml } from '../client-html' -import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model' -import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' import { PluginTranslation } from '../../../shared/models/plugins/plugin-translation.model' -import { buildRegisterHelpers, reinitVideoConstants } from './register-helpers' +import { RegisterHelpersStore } from './register-helpers-store' +import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' export interface RegisteredPlugin { npmName: string @@ -59,10 +58,11 @@ export class PluginManager implements ServerHook { private static instance: PluginManager private registeredPlugins: { [name: string]: RegisteredPlugin } = {} - private settings: { [name: string]: RegisterServerSettingOptions[] } = {} private hooks: { [name: string]: HookInformationValue[] } = {} private translations: PluginLocalesTranslations = {} + private registerHelpersStore: { [npmName: string]: RegisterHelpersStore } = {} + private constructor () { } @@ -103,7 +103,17 @@ export class PluginManager implements ServerHook { } getRegisteredSettings (npmName: string) { - return this.settings[npmName] || [] + const store = this.registerHelpersStore[npmName] + if (store) return store.getSettings() + + return [] + } + + getRouter (npmName: string) { + const store = this.registerHelpersStore[npmName] + if (!store) return null + + return store.getRouter() } getTranslations (locale: string) { @@ -164,7 +174,6 @@ export class PluginManager implements ServerHook { } delete this.registeredPlugins[plugin.npmName] - delete this.settings[plugin.npmName] this.deleteTranslations(plugin.npmName) @@ -176,7 +185,10 @@ export class PluginManager implements ServerHook { this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName) } - reinitVideoConstants(plugin.npmName) + const store = this.registerHelpersStore[plugin.npmName] + store.reinitVideoConstants(plugin.npmName) + + delete this.registerHelpersStore[plugin.npmName] logger.info('Regenerating registered plugin CSS to global file.') await this.regeneratePluginGlobalCSS() @@ -429,34 +441,21 @@ export class PluginManager implements ServerHook { // ###################### Generate register helpers ###################### private getRegisterHelpers (npmName: string, plugin: PluginModel): RegisterServerOptions { - const registerHook = (options: RegisterServerHookOptions) => { - if (serverHookObject[options.target] !== true) { - logger.warn('Unknown hook %s of plugin %s. Skipping.', options.target, npmName) - return - } - + const onHookAdded = (options: RegisterServerHookOptions) => { if (!this.hooks[options.target]) this.hooks[options.target] = [] this.hooks[options.target].push({ - npmName, + npmName: npmName, pluginName: plugin.name, handler: options.handler, priority: options.priority || 0 }) } - const registerSetting = (options: RegisterServerSettingOptions) => { - if (!this.settings[npmName]) this.settings[npmName] = [] - - this.settings[npmName].push(options) - } - - const registerHelpers = buildRegisterHelpers(npmName, plugin) + const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this)) + this.registerHelpersStore[npmName] = registerHelpersStore - return Object.assign(registerHelpers, { - registerHook, - registerSetting - }) + return registerHelpersStore.buildRegisterHelpers() } private sanitizeAndCheckPackageJSONOrThrow (packageJSON: PluginPackageJson, pluginType: PluginType) { diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers-store.ts new file mode 100644 index 000000000..c76c0161a --- /dev/null +++ b/server/lib/plugins/register-helpers-store.ts @@ -0,0 +1,235 @@ +import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model' +import { PluginModel } from '@server/models/server/plugin' +import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model' +import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model' +import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '@server/initializers/constants' +import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model' +import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model' +import { RegisterServerOptions } from '@server/typings/plugins' +import { buildPluginHelpers } from './plugin-helpers' +import { logger } from '@server/helpers/logger' +import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' +import { serverHookObject } from '@shared/models/plugins/server-hook.model' +import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model' +import * as express from 'express' + +type AlterableVideoConstant = 'language' | 'licence' | 'category' +type VideoConstant = { [key in number | string]: string } + +type UpdatedVideoConstant = { + [name in AlterableVideoConstant]: { + added: { key: number | string, label: string }[] + deleted: { key: number | string, label: string }[] + } +} + +export class RegisterHelpersStore { + private readonly updatedVideoConstants: UpdatedVideoConstant = { + language: { added: [], deleted: [] }, + licence: { added: [], deleted: [] }, + category: { added: [], deleted: [] } + } + + private readonly settings: RegisterServerSettingOptions[] = [] + + private readonly router: express.Router + + constructor ( + private readonly npmName: string, + private readonly plugin: PluginModel, + private readonly onHookAdded: (options: RegisterServerHookOptions) => void + ) { + this.router = express.Router() + } + + buildRegisterHelpers (): RegisterServerOptions { + const registerHook = this.buildRegisterHook() + const registerSetting = this.buildRegisterSetting() + + const getRouter = this.buildGetRouter() + + const settingsManager = this.buildSettingsManager() + const storageManager = this.buildStorageManager() + + const videoLanguageManager = this.buildVideoLanguageManager() + + const videoLicenceManager = this.buildVideoLicenceManager() + const videoCategoryManager = this.buildVideoCategoryManager() + + const peertubeHelpers = buildPluginHelpers(this.npmName) + + return { + registerHook, + registerSetting, + + getRouter, + + settingsManager, + storageManager, + + videoLanguageManager, + videoCategoryManager, + videoLicenceManager, + + peertubeHelpers + } + } + + reinitVideoConstants (npmName: string) { + const hash = { + language: VIDEO_LANGUAGES, + licence: VIDEO_LICENCES, + category: VIDEO_CATEGORIES + } + const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category' ] + + for (const type of types) { + const updatedConstants = this.updatedVideoConstants[type][npmName] + if (!updatedConstants) continue + + for (const added of updatedConstants.added) { + delete hash[type][added.key] + } + + for (const deleted of updatedConstants.deleted) { + hash[type][deleted.key] = deleted.label + } + + delete this.updatedVideoConstants[type][npmName] + } + } + + getSettings () { + return this.settings + } + + getRouter () { + return this.router + } + + private buildGetRouter () { + return () => this.router + } + + private buildRegisterSetting () { + return (options: RegisterServerSettingOptions) => { + this.settings.push(options) + } + } + + private buildRegisterHook () { + return (options: RegisterServerHookOptions) => { + if (serverHookObject[options.target] !== true) { + logger.warn('Unknown hook %s of plugin %s. Skipping.', options.target, this.npmName) + return + } + + return this.onHookAdded(options) + } + } + + private buildSettingsManager (): PluginSettingsManager { + return { + getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name), + + setSetting: (name: string, value: string) => PluginModel.setSetting(this.plugin.name, this.plugin.type, name, value) + } + } + + private buildStorageManager (): PluginStorageManager { + return { + getData: (key: string) => PluginModel.getData(this.plugin.name, this.plugin.type, key), + + storeData: (key: string, data: any) => PluginModel.storeData(this.plugin.name, this.plugin.type, key, data) + } + } + + private buildVideoLanguageManager (): PluginVideoLanguageManager { + return { + addLanguage: (key: string, label: string) => { + return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label }) + }, + + deleteLanguage: (key: string) => { + return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key }) + } + } + } + + private buildVideoCategoryManager (): PluginVideoCategoryManager { + return { + addCategory: (key: number, label: string) => { + return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label }) + }, + + deleteCategory: (key: number) => { + return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key }) + } + } + } + + private buildVideoLicenceManager (): PluginVideoLicenceManager { + return { + addLicence: (key: number, label: string) => { + return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label }) + }, + + deleteLicence: (key: number) => { + return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key }) + } + } + } + + private addConstant (parameters: { + npmName: string + type: AlterableVideoConstant + obj: VideoConstant + key: T + label: string + }) { + const { npmName, type, obj, key, label } = parameters + + if (obj[key]) { + logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key) + return false + } + + if (!this.updatedVideoConstants[type][npmName]) { + this.updatedVideoConstants[type][npmName] = { + added: [], + deleted: [] + } + } + + this.updatedVideoConstants[type][npmName].added.push({ key, label }) + obj[key] = label + + return true + } + + private deleteConstant (parameters: { + npmName: string + type: AlterableVideoConstant + obj: VideoConstant + key: T + }) { + const { npmName, type, obj, key } = parameters + + if (!obj[key]) { + logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key) + return false + } + + if (!this.updatedVideoConstants[type][npmName]) { + this.updatedVideoConstants[type][npmName] = { + added: [], + deleted: [] + } + } + + this.updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] }) + delete obj[key] + + return true + } +} diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts deleted file mode 100644 index 4c0935a05..000000000 --- a/server/lib/plugins/register-helpers.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model' -import { PluginModel } from '@server/models/server/plugin' -import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model' -import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model' -import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '@server/initializers/constants' -import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model' -import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model' -import { RegisterServerOptions } from '@server/typings/plugins' -import { buildPluginHelpers } from './plugin-helpers' -import { logger } from '@server/helpers/logger' - -type AlterableVideoConstant = 'language' | 'licence' | 'category' -type VideoConstant = { [key in number | string]: string } -type UpdatedVideoConstant = { - [name in AlterableVideoConstant]: { - [npmName: string]: { - added: { key: number | string, label: string }[] - deleted: { key: number | string, label: string }[] - } - } -} - -const updatedVideoConstants: UpdatedVideoConstant = { - language: {}, - licence: {}, - category: {} -} - -function buildRegisterHelpers (npmName: string, plugin: PluginModel): Omit { - const settingsManager = buildSettingsManager(plugin) - const storageManager = buildStorageManager(plugin) - - const videoLanguageManager = buildVideoLanguageManager(npmName) - - const videoCategoryManager = buildVideoCategoryManager(npmName) - const videoLicenceManager = buildVideoLicenceManager(npmName) - - const peertubeHelpers = buildPluginHelpers(npmName) - - return { - settingsManager, - storageManager, - videoLanguageManager, - videoCategoryManager, - videoLicenceManager, - peertubeHelpers - } -} - -function reinitVideoConstants (npmName: string) { - const hash = { - language: VIDEO_LANGUAGES, - licence: VIDEO_LICENCES, - category: VIDEO_CATEGORIES - } - const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category' ] - - for (const type of types) { - const updatedConstants = updatedVideoConstants[type][npmName] - if (!updatedConstants) continue - - for (const added of updatedConstants.added) { - delete hash[type][added.key] - } - - for (const deleted of updatedConstants.deleted) { - hash[type][deleted.key] = deleted.label - } - - delete updatedVideoConstants[type][npmName] - } -} - -export { - buildRegisterHelpers, - reinitVideoConstants -} - -// --------------------------------------------------------------------------- - -function buildSettingsManager (plugin: PluginModel): PluginSettingsManager { - return { - getSetting: (name: string) => PluginModel.getSetting(plugin.name, plugin.type, name), - - setSetting: (name: string, value: string) => PluginModel.setSetting(plugin.name, plugin.type, name, value) - } -} - -function buildStorageManager (plugin: PluginModel): PluginStorageManager { - return { - getData: (key: string) => PluginModel.getData(plugin.name, plugin.type, key), - - storeData: (key: string, data: any) => PluginModel.storeData(plugin.name, plugin.type, key, data) - } -} - -function buildVideoLanguageManager (npmName: string): PluginVideoLanguageManager { - return { - addLanguage: (key: string, label: string) => addConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label }), - - deleteLanguage: (key: string) => deleteConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key }) - } -} - -function buildVideoCategoryManager (npmName: string): PluginVideoCategoryManager { - return { - addCategory: (key: number, label: string) => { - return addConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label }) - }, - - deleteCategory: (key: number) => { - return deleteConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key }) - } - } -} - -function buildVideoLicenceManager (npmName: string): PluginVideoLicenceManager { - return { - addLicence: (key: number, label: string) => { - return addConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key, label }) - }, - - deleteLicence: (key: number) => { - return deleteConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key }) - } - } -} - -function addConstant (parameters: { - npmName: string - type: AlterableVideoConstant - obj: VideoConstant - key: T - label: string -}) { - const { npmName, type, obj, key, label } = parameters - - if (obj[key]) { - logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key) - return false - } - - if (!updatedVideoConstants[type][npmName]) { - updatedVideoConstants[type][npmName] = { - added: [], - deleted: [] - } - } - - updatedVideoConstants[type][npmName].added.push({ key, label }) - obj[key] = label - - return true -} - -function deleteConstant (parameters: { - npmName: string - type: AlterableVideoConstant - obj: VideoConstant - key: T -}) { - const { npmName, type, obj, key } = parameters - - if (!obj[key]) { - logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key) - return false - } - - if (!updatedVideoConstants[type][npmName]) { - updatedVideoConstants[type][npmName] = { - added: [], - deleted: [] - } - } - - updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] }) - delete obj[key] - - return true -} -- cgit v1.2.3