aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/plugins/plugin-manager.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/plugins/plugin-manager.ts')
-rw-r--r--server/lib/plugins/plugin-manager.ts111
1 files changed, 74 insertions, 37 deletions
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 8496979f8..3d8375acd 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -1,6 +1,5 @@
1import { PluginModel } from '../../models/server/plugin' 1import { PluginModel } from '../../models/server/plugin'
2import { logger } from '../../helpers/logger' 2import { logger } from '../../helpers/logger'
3import { RegisterHookOptions } from '../../../shared/models/plugins/register.model'
4import { basename, join } from 'path' 3import { basename, join } from 'path'
5import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
6import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' 5import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins'
@@ -11,7 +10,9 @@ import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants'
11import { PluginType } from '../../../shared/models/plugins/plugin.type' 10import { PluginType } from '../../../shared/models/plugins/plugin.type'
12import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' 11import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
13import { outputFile } from 'fs-extra' 12import { outputFile } from 'fs-extra'
14import { ServerConfigPlugin } from '../../../shared/models/server' 13import { RegisterSettingOptions } from '../../../shared/models/plugins/register-setting.model'
14import { RegisterHookOptions } from '../../../shared/models/plugins/register-hook.model'
15import { PluginSettingsManager } from '../../../shared/models/plugins/plugin-settings-manager.model'
15 16
16export interface RegisteredPlugin { 17export interface RegisteredPlugin {
17 name: string 18 name: string
@@ -43,26 +44,13 @@ export class PluginManager {
43 private static instance: PluginManager 44 private static instance: PluginManager
44 45
45 private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {} 46 private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {}
47 private settings: { [ name: string ]: RegisterSettingOptions[] } = {}
46 private hooks: { [ name: string ]: HookInformationValue[] } = {} 48 private hooks: { [ name: string ]: HookInformationValue[] } = {}
47 49
48 private constructor () { 50 private constructor () {
49 } 51 }
50 52
51 async registerPluginsAndThemes () { 53 // ###################### Getters ######################
52 await this.resetCSSGlobalFile()
53
54 const plugins = await PluginModel.listEnabledPluginsAndThemes()
55
56 for (const plugin of plugins) {
57 try {
58 await this.registerPluginOrTheme(plugin)
59 } catch (err) {
60 logger.error('Cannot register plugin %s, skipping.', plugin.name, { err })
61 }
62 }
63
64 this.sortHooksByPriority()
65 }
66 54
67 getRegisteredPluginOrTheme (name: string) { 55 getRegisteredPluginOrTheme (name: string) {
68 return this.registeredPlugins[name] 56 return this.registeredPlugins[name]
@@ -92,6 +80,12 @@ export class PluginManager {
92 return this.getRegisteredPluginsOrThemes(PluginType.THEME) 80 return this.getRegisteredPluginsOrThemes(PluginType.THEME)
93 } 81 }
94 82
83 getSettings (name: string) {
84 return this.settings[name] || []
85 }
86
87 // ###################### Hooks ######################
88
95 async runHook (hookName: string, param?: any) { 89 async runHook (hookName: string, param?: any) {
96 let result = param 90 let result = param
97 91
@@ -99,8 +93,11 @@ export class PluginManager {
99 93
100 for (const hook of this.hooks[hookName]) { 94 for (const hook of this.hooks[hookName]) {
101 try { 95 try {
102 if (wait) result = await hook.handler(param) 96 if (wait) {
103 else result = hook.handler() 97 result = await hook.handler(param)
98 } else {
99 result = hook.handler()
100 }
104 } catch (err) { 101 } catch (err) {
105 logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err }) 102 logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
106 } 103 }
@@ -109,6 +106,24 @@ export class PluginManager {
109 return result 106 return result
110 } 107 }
111 108
109 // ###################### Registration ######################
110
111 async registerPluginsAndThemes () {
112 await this.resetCSSGlobalFile()
113
114 const plugins = await PluginModel.listEnabledPluginsAndThemes()
115
116 for (const plugin of plugins) {
117 try {
118 await this.registerPluginOrTheme(plugin)
119 } catch (err) {
120 logger.error('Cannot register plugin %s, skipping.', plugin.name, { err })
121 }
122 }
123
124 this.sortHooksByPriority()
125 }
126
112 async unregister (name: string) { 127 async unregister (name: string) {
113 const plugin = this.getRegisteredPlugin(name) 128 const plugin = this.getRegisteredPlugin(name)
114 129
@@ -133,7 +148,9 @@ export class PluginManager {
133 await this.regeneratePluginGlobalCSS() 148 await this.regeneratePluginGlobalCSS()
134 } 149 }
135 150
136 async install (toInstall: string, version: string, fromDisk = false) { 151 // ###################### Installation ######################
152
153 async install (toInstall: string, version?: string, fromDisk = false) {
137 let plugin: PluginModel 154 let plugin: PluginModel
138 let name: string 155 let name: string
139 156
@@ -206,6 +223,8 @@ export class PluginManager {
206 logger.info('Plugin %s uninstalled.', packageName) 223 logger.info('Plugin %s uninstalled.', packageName)
207 } 224 }
208 225
226 // ###################### Private register ######################
227
209 private async registerPluginOrTheme (plugin: PluginModel) { 228 private async registerPluginOrTheme (plugin: PluginModel) {
210 logger.info('Registering plugin or theme %s.', plugin.name) 229 logger.info('Registering plugin or theme %s.', plugin.name)
211 230
@@ -251,13 +270,25 @@ export class PluginManager {
251 }) 270 })
252 } 271 }
253 272
273 const registerSetting = (options: RegisterSettingOptions) => {
274 if (!this.settings[plugin.name]) this.settings[plugin.name] = []
275
276 this.settings[plugin.name].push(options)
277 }
278
279 const settingsManager: PluginSettingsManager = {
280 getSetting: (name: string) => PluginModel.getSetting(plugin.name, name),
281
282 setSetting: (name: string, value: string) => PluginModel.setSetting(plugin.name, name, value)
283 }
284
254 const library: PluginLibrary = require(join(pluginPath, packageJSON.library)) 285 const library: PluginLibrary = require(join(pluginPath, packageJSON.library))
255 286
256 if (!isLibraryCodeValid(library)) { 287 if (!isLibraryCodeValid(library)) {
257 throw new Error('Library code is not valid (miss register or unregister function)') 288 throw new Error('Library code is not valid (miss register or unregister function)')
258 } 289 }
259 290
260 library.register({ registerHook }) 291 library.register({ registerHook, registerSetting, settingsManager })
261 292
262 logger.info('Add plugin %s CSS to global file.', plugin.name) 293 logger.info('Add plugin %s CSS to global file.', plugin.name)
263 294
@@ -266,13 +297,7 @@ export class PluginManager {
266 return library 297 return library
267 } 298 }
268 299
269 private sortHooksByPriority () { 300 // ###################### CSS ######################
270 for (const hookName of Object.keys(this.hooks)) {
271 this.hooks[hookName].sort((a, b) => {
272 return b.priority - a.priority
273 })
274 }
275 }
276 301
277 private resetCSSGlobalFile () { 302 private resetCSSGlobalFile () {
278 return outputFile(PLUGIN_GLOBAL_CSS_PATH, '') 303 return outputFile(PLUGIN_GLOBAL_CSS_PATH, '')
@@ -296,6 +321,26 @@ export class PluginManager {
296 }) 321 })
297 } 322 }
298 323
324 private async regeneratePluginGlobalCSS () {
325 await this.resetCSSGlobalFile()
326
327 for (const key of Object.keys(this.registeredPlugins)) {
328 const plugin = this.registeredPlugins[key]
329
330 await this.addCSSToGlobalFile(plugin.path, plugin.css)
331 }
332 }
333
334 // ###################### Utils ######################
335
336 private sortHooksByPriority () {
337 for (const hookName of Object.keys(this.hooks)) {
338 this.hooks[hookName].sort((a, b) => {
339 return b.priority - a.priority
340 })
341 }
342 }
343
299 private getPackageJSON (pluginName: string, pluginType: PluginType) { 344 private getPackageJSON (pluginName: string, pluginType: PluginType) {
300 const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json') 345 const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json')
301 346
@@ -312,15 +357,7 @@ export class PluginManager {
312 return name.replace(/^peertube-((theme)|(plugin))-/, '') 357 return name.replace(/^peertube-((theme)|(plugin))-/, '')
313 } 358 }
314 359
315 private async regeneratePluginGlobalCSS () { 360 // ###################### Private getters ######################
316 await this.resetCSSGlobalFile()
317
318 for (const key of Object.keys(this.registeredPlugins)) {
319 const plugin = this.registeredPlugins[key]
320
321 await this.addCSSToGlobalFile(plugin.path, plugin.css)
322 }
323 }
324 361
325 private getRegisteredPluginsOrThemes (type: PluginType) { 362 private getRegisteredPluginsOrThemes (type: PluginType) {
326 const plugins: RegisteredPlugin[] = [] 363 const plugins: RegisteredPlugin[] = []