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.ts76
1 files changed, 73 insertions, 3 deletions
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index b48ecc991..533ed4391 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -1,7 +1,7 @@
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' 3import { RegisterHookOptions } from '../../../shared/models/plugins/register.model'
4import { 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 { PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
@@ -9,6 +9,7 @@ import { PluginLibrary } from '../../../shared/models/plugins/plugin-library.mod
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'
12 13
13export interface RegisteredPlugin { 14export interface RegisteredPlugin {
14 name: string 15 name: string
@@ -84,11 +85,63 @@ export class PluginManager {
84 await plugin.unregister() 85 await plugin.unregister()
85 } 86 }
86 87
88 async install (toInstall: string, version: string, fromDisk = false) {
89 let plugin: PluginModel
90 let name: string
91
92 logger.info('Installing plugin %s.', toInstall)
93
94 try {
95 fromDisk
96 ? await installNpmPluginFromDisk(toInstall)
97 : await installNpmPlugin(toInstall, version)
98
99 name = fromDisk ? basename(toInstall) : toInstall
100 const pluginType = name.startsWith('peertube-theme-') ? PluginType.THEME : PluginType.PLUGIN
101 const pluginName = this.normalizePluginName(name)
102
103 const packageJSON = this.getPackageJSON(pluginName, pluginType)
104 if (!isPackageJSONValid(packageJSON, pluginType)) {
105 throw new Error('PackageJSON is invalid.')
106 }
107
108 [ plugin ] = await PluginModel.upsert({
109 name: pluginName,
110 description: packageJSON.description,
111 type: pluginType,
112 version: packageJSON.version,
113 enabled: true,
114 uninstalled: false,
115 peertubeEngine: packageJSON.engine.peertube
116 }, { returning: true })
117 } catch (err) {
118 logger.error('Cannot install plugin %s, removing it...', toInstall, { err })
119
120 try {
121 await removeNpmPlugin(name)
122 } catch (err) {
123 logger.error('Cannot remove plugin %s after failed installation.', toInstall, { err })
124 }
125
126 throw err
127 }
128
129 logger.info('Successful installation of plugin %s.', toInstall)
130
131 await this.registerPluginOrTheme(plugin)
132 }
133
134 async uninstall (packageName: string) {
135 await PluginModel.uninstall(this.normalizePluginName(packageName))
136
137 await removeNpmPlugin(packageName)
138 }
139
87 private async registerPluginOrTheme (plugin: PluginModel) { 140 private async registerPluginOrTheme (plugin: PluginModel) {
88 logger.info('Registering plugin or theme %s.', plugin.name) 141 logger.info('Registering plugin or theme %s.', plugin.name)
89 142
90 const pluginPath = join(CONFIG.STORAGE.PLUGINS_DIR, plugin.name, plugin.version) 143 const packageJSON = this.getPackageJSON(plugin.name, plugin.type)
91 const packageJSON: PluginPackageJson = require(join(pluginPath, 'package.json')) 144 const pluginPath = this.getPluginPath(plugin.name, plugin.type)
92 145
93 if (!isPackageJSONValid(packageJSON, plugin.type)) { 146 if (!isPackageJSONValid(packageJSON, plugin.type)) {
94 throw new Error('Package.JSON is invalid.') 147 throw new Error('Package.JSON is invalid.')
@@ -124,6 +177,7 @@ export class PluginManager {
124 } 177 }
125 178
126 const library: PluginLibrary = require(join(pluginPath, packageJSON.library)) 179 const library: PluginLibrary = require(join(pluginPath, packageJSON.library))
180
127 if (!isLibraryCodeValid(library)) { 181 if (!isLibraryCodeValid(library)) {
128 throw new Error('Library code is not valid (miss register or unregister function)') 182 throw new Error('Library code is not valid (miss register or unregister function)')
129 } 183 }
@@ -163,6 +217,22 @@ export class PluginManager {
163 }) 217 })
164 } 218 }
165 219
220 private getPackageJSON (pluginName: string, pluginType: PluginType) {
221 const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json')
222
223 return require(pluginPath) as PluginPackageJson
224 }
225
226 private getPluginPath (pluginName: string, pluginType: PluginType) {
227 const prefix = pluginType === PluginType.PLUGIN ? 'peertube-plugin-' : 'peertube-theme-'
228
229 return join(CONFIG.STORAGE.PLUGINS_DIR, 'node_modules', prefix + pluginName)
230 }
231
232 private normalizePluginName (name: string) {
233 return name.replace(/^peertube-((theme)|(plugin))-/, '')
234 }
235
166 static get Instance () { 236 static get Instance () {
167 return this.instance || (this.instance = new this()) 237 return this.instance || (this.instance = new this())
168 } 238 }