aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/plugins/plugin-manager.ts63
1 files changed, 59 insertions, 4 deletions
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 533ed4391..b898e64fa 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -4,12 +4,13 @@ import { RegisterHookOptions } from '../../../shared/models/plugins/register.mod
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { CONFIG } from '../../initializers/config' 5import { CONFIG } from '../../initializers/config'
6import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' 6import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins'
7import { PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model' 7import { ClientScript, PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
8import { PluginLibrary } from '../../../shared/models/plugins/plugin-library.model' 8import { PluginLibrary } from '../../../shared/models/plugins/plugin-library.model'
9import { createReadStream, createWriteStream } from 'fs' 9import { createReadStream, createWriteStream } from 'fs'
10import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants' 10import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants'
11import { PluginType } from '../../../shared/models/plugins/plugin.type' 11import { PluginType } from '../../../shared/models/plugins/plugin.type'
12import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' 12import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
13import { outputFile } from 'fs-extra'
13 14
14export interface RegisteredPlugin { 15export interface RegisteredPlugin {
15 name: string 16 name: string
@@ -22,6 +23,7 @@ export interface RegisteredPlugin {
22 path: string 23 path: string
23 24
24 staticDirs: { [name: string]: string } 25 staticDirs: { [name: string]: string }
26 clientScripts: { [name: string]: ClientScript }
25 27
26 css: string[] 28 css: string[]
27 29
@@ -46,6 +48,8 @@ export class PluginManager {
46 } 48 }
47 49
48 async registerPlugins () { 50 async registerPlugins () {
51 await this.resetCSSGlobalFile()
52
49 const plugins = await PluginModel.listEnabledPluginsAndThemes() 53 const plugins = await PluginModel.listEnabledPluginsAndThemes()
50 54
51 for (const plugin of plugins) { 55 for (const plugin of plugins) {
@@ -83,6 +87,16 @@ export class PluginManager {
83 } 87 }
84 88
85 await plugin.unregister() 89 await plugin.unregister()
90
91 // Remove hooks of this plugin
92 for (const key of Object.keys(this.hooks)) {
93 this.hooks[key] = this.hooks[key].filter(h => h.pluginName !== name)
94 }
95
96 delete this.registeredPlugins[plugin.name]
97
98 logger.info('Regenerating registered plugin CSS to global file.')
99 await this.regeneratePluginGlobalCSS()
86 } 100 }
87 101
88 async install (toInstall: string, version: string, fromDisk = false) { 102 async install (toInstall: string, version: string, fromDisk = false) {
@@ -132,9 +146,30 @@ export class PluginManager {
132 } 146 }
133 147
134 async uninstall (packageName: string) { 148 async uninstall (packageName: string) {
135 await PluginModel.uninstall(this.normalizePluginName(packageName)) 149 logger.info('Uninstalling plugin %s.', packageName)
150
151 const pluginName = this.normalizePluginName(packageName)
152
153 try {
154 await this.unregister(pluginName)
155 } catch (err) {
156 logger.warn('Cannot unregister plugin %s.', pluginName, { err })
157 }
158
159 const plugin = await PluginModel.load(pluginName)
160 if (!plugin || plugin.uninstalled === true) {
161 logger.error('Cannot uninstall plugin %s: it does not exist or is already uninstalled.', packageName)
162 return
163 }
164
165 plugin.enabled = false
166 plugin.uninstalled = true
167
168 await plugin.save()
136 169
137 await removeNpmPlugin(packageName) 170 await removeNpmPlugin(packageName)
171
172 logger.info('Plugin %s uninstalled.', packageName)
138 } 173 }
139 174
140 private async registerPluginOrTheme (plugin: PluginModel) { 175 private async registerPluginOrTheme (plugin: PluginModel) {
@@ -152,6 +187,11 @@ export class PluginManager {
152 library = await this.registerPlugin(plugin, pluginPath, packageJSON) 187 library = await this.registerPlugin(plugin, pluginPath, packageJSON)
153 } 188 }
154 189
190 const clientScripts: { [id: string]: ClientScript } = {}
191 for (const c of packageJSON.clientScripts) {
192 clientScripts[c.script] = c
193 }
194
155 this.registeredPlugins[ plugin.name ] = { 195 this.registeredPlugins[ plugin.name ] = {
156 name: plugin.name, 196 name: plugin.name,
157 type: plugin.type, 197 type: plugin.type,
@@ -160,6 +200,7 @@ export class PluginManager {
160 peertubeEngine: plugin.peertubeEngine, 200 peertubeEngine: plugin.peertubeEngine,
161 path: pluginPath, 201 path: pluginPath,
162 staticDirs: packageJSON.staticDirs, 202 staticDirs: packageJSON.staticDirs,
203 clientScripts,
163 css: packageJSON.css, 204 css: packageJSON.css,
164 unregister: library ? library.unregister : undefined 205 unregister: library ? library.unregister : undefined
165 } 206 }
@@ -199,6 +240,10 @@ export class PluginManager {
199 } 240 }
200 } 241 }
201 242
243 private resetCSSGlobalFile () {
244 return outputFile(PLUGIN_GLOBAL_CSS_PATH, '')
245 }
246
202 private async addCSSToGlobalFile (pluginPath: string, cssRelativePaths: string[]) { 247 private async addCSSToGlobalFile (pluginPath: string, cssRelativePaths: string[]) {
203 for (const cssPath of cssRelativePaths) { 248 for (const cssPath of cssRelativePaths) {
204 await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH) 249 await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH)
@@ -207,8 +252,8 @@ export class PluginManager {
207 252
208 private concatFiles (input: string, output: string) { 253 private concatFiles (input: string, output: string) {
209 return new Promise<void>((res, rej) => { 254 return new Promise<void>((res, rej) => {
210 const outputStream = createWriteStream(input) 255 const inputStream = createReadStream(input)
211 const inputStream = createReadStream(output) 256 const outputStream = createWriteStream(output, { flags: 'a' })
212 257
213 inputStream.pipe(outputStream) 258 inputStream.pipe(outputStream)
214 259
@@ -233,6 +278,16 @@ export class PluginManager {
233 return name.replace(/^peertube-((theme)|(plugin))-/, '') 278 return name.replace(/^peertube-((theme)|(plugin))-/, '')
234 } 279 }
235 280
281 private async regeneratePluginGlobalCSS () {
282 await this.resetCSSGlobalFile()
283
284 for (const key of Object.keys(this.registeredPlugins)) {
285 const plugin = this.registeredPlugins[key]
286
287 await this.addCSSToGlobalFile(plugin.path, plugin.css)
288 }
289 }
290
236 static get Instance () { 291 static get Instance () {
237 return this.instance || (this.instance = new this()) 292 return this.instance || (this.instance = new this())
238 } 293 }