aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-07-11 14:40:19 +0200
committerChocobozzz <chocobozzz@cpy.re>2019-07-24 10:58:16 +0200
commitdba85a1e9e9f603ba52e1ea42deaf3fdd799b1d8 (patch)
tree7695023d90b78f972abafc718346c50264587ff5 /server
parentd00dc28dd73ad9dd419d5a5ac6ac747cefbc6e8b (diff)
downloadPeerTube-dba85a1e9e9f603ba52e1ea42deaf3fdd799b1d8.tar.gz
PeerTube-dba85a1e9e9f603ba52e1ea42deaf3fdd799b1d8.tar.zst
PeerTube-dba85a1e9e9f603ba52e1ea42deaf3fdd799b1d8.zip
WIP plugins: add plugin settings/uninstall in client
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/plugins.ts27
-rw-r--r--server/helpers/custom-validators/plugins.ts7
-rw-r--r--server/lib/plugins/plugin-manager.ts25
-rw-r--r--server/middlewares/validators/plugins.ts12
-rw-r--r--server/models/server/plugin.ts28
5 files changed, 68 insertions, 31 deletions
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts
index 89cc67f54..f17e8cab9 100644
--- a/server/controllers/api/plugins.ts
+++ b/server/controllers/api/plugins.ts
@@ -12,7 +12,7 @@ import { pluginsSortValidator } from '../../middlewares/validators'
12import { PluginModel } from '../../models/server/plugin' 12import { PluginModel } from '../../models/server/plugin'
13import { UserRight } from '../../../shared/models/users' 13import { UserRight } from '../../../shared/models/users'
14import { 14import {
15 enabledPluginValidator, 15 existingPluginValidator,
16 installPluginValidator, 16 installPluginValidator,
17 listPluginsValidator, 17 listPluginsValidator,
18 uninstallPluginValidator, 18 uninstallPluginValidator,
@@ -35,18 +35,25 @@ pluginRouter.get('/',
35 asyncMiddleware(listPlugins) 35 asyncMiddleware(listPlugins)
36) 36)
37 37
38pluginRouter.get('/:pluginName/settings', 38pluginRouter.get('/:npmName',
39 authenticate, 39 authenticate,
40 ensureUserHasRight(UserRight.MANAGE_PLUGINS), 40 ensureUserHasRight(UserRight.MANAGE_PLUGINS),
41 asyncMiddleware(enabledPluginValidator), 41 asyncMiddleware(existingPluginValidator),
42 asyncMiddleware(listPluginSettings) 42 getPlugin
43) 43)
44 44
45pluginRouter.put('/:pluginName/settings', 45pluginRouter.get('/:npmName/registered-settings',
46 authenticate,
47 ensureUserHasRight(UserRight.MANAGE_PLUGINS),
48 asyncMiddleware(existingPluginValidator),
49 asyncMiddleware(getPluginRegisteredSettings)
50)
51
52pluginRouter.put('/:npmName/settings',
46 authenticate, 53 authenticate,
47 ensureUserHasRight(UserRight.MANAGE_PLUGINS), 54 ensureUserHasRight(UserRight.MANAGE_PLUGINS),
48 updatePluginSettingsValidator, 55 updatePluginSettingsValidator,
49 asyncMiddleware(enabledPluginValidator), 56 asyncMiddleware(existingPluginValidator),
50 asyncMiddleware(updatePluginSettings) 57 asyncMiddleware(updatePluginSettings)
51) 58)
52 59
@@ -85,6 +92,12 @@ async function listPlugins (req: express.Request, res: express.Response) {
85 return res.json(getFormattedObjects(resultList.data, resultList.total)) 92 return res.json(getFormattedObjects(resultList.data, resultList.total))
86} 93}
87 94
95function getPlugin (req: express.Request, res: express.Response) {
96 const plugin = res.locals.plugin
97
98 return res.json(plugin.toFormattedJSON())
99}
100
88async function installPlugin (req: express.Request, res: express.Response) { 101async function installPlugin (req: express.Request, res: express.Response) {
89 const body: InstallPlugin = req.body 102 const body: InstallPlugin = req.body
90 103
@@ -101,7 +114,7 @@ async function uninstallPlugin (req: express.Request, res: express.Response) {
101 return res.sendStatus(204) 114 return res.sendStatus(204)
102} 115}
103 116
104async function listPluginSettings (req: express.Request, res: express.Response) { 117async function getPluginRegisteredSettings (req: express.Request, res: express.Response) {
105 const plugin = res.locals.plugin 118 const plugin = res.locals.plugin
106 119
107 const settings = await PluginManager.Instance.getSettings(plugin.name) 120 const settings = await PluginManager.Instance.getSettings(plugin.name)
diff --git a/server/helpers/custom-validators/plugins.ts b/server/helpers/custom-validators/plugins.ts
index 4ab5f9ce8..064af9ead 100644
--- a/server/helpers/custom-validators/plugins.ts
+++ b/server/helpers/custom-validators/plugins.ts
@@ -41,6 +41,10 @@ function isPluginEngineValid (engine: any) {
41 return exists(engine) && exists(engine.peertube) 41 return exists(engine) && exists(engine.peertube)
42} 42}
43 43
44function isPluginHomepage (value: string) {
45 return isUrlValid(value)
46}
47
44function isStaticDirectoriesValid (staticDirs: any) { 48function isStaticDirectoriesValid (staticDirs: any) {
45 if (!exists(staticDirs) || typeof staticDirs !== 'object') return false 49 if (!exists(staticDirs) || typeof staticDirs !== 'object') return false
46 50
@@ -70,7 +74,7 @@ function isPackageJSONValid (packageJSON: PluginPackageJson, pluginType: PluginT
70 return isNpmPluginNameValid(packageJSON.name) && 74 return isNpmPluginNameValid(packageJSON.name) &&
71 isPluginDescriptionValid(packageJSON.description) && 75 isPluginDescriptionValid(packageJSON.description) &&
72 isPluginEngineValid(packageJSON.engine) && 76 isPluginEngineValid(packageJSON.engine) &&
73 isUrlValid(packageJSON.homepage) && 77 isPluginHomepage(packageJSON.homepage) &&
74 exists(packageJSON.author) && 78 exists(packageJSON.author) &&
75 isUrlValid(packageJSON.bugs) && 79 isUrlValid(packageJSON.bugs) &&
76 (pluginType === PluginType.THEME || isSafePath(packageJSON.library)) && 80 (pluginType === PluginType.THEME || isSafePath(packageJSON.library)) &&
@@ -88,6 +92,7 @@ export {
88 isPluginTypeValid, 92 isPluginTypeValid,
89 isPackageJSONValid, 93 isPackageJSONValid,
90 isThemeValid, 94 isThemeValid,
95 isPluginHomepage,
91 isPluginVersionValid, 96 isPluginVersionValid,
92 isPluginNameValid, 97 isPluginNameValid,
93 isPluginDescriptionValid, 98 isPluginDescriptionValid,
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 3d8375acd..8cdeff446 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -89,6 +89,8 @@ export class PluginManager {
89 async runHook (hookName: string, param?: any) { 89 async runHook (hookName: string, param?: any) {
90 let result = param 90 let result = param
91 91
92 if (!this.hooks[hookName]) return result
93
92 const wait = hookName.startsWith('static:') 94 const wait = hookName.startsWith('static:')
93 95
94 for (const hook of this.hooks[hookName]) { 96 for (const hook of this.hooks[hookName]) {
@@ -162,8 +164,8 @@ export class PluginManager {
162 : await installNpmPlugin(toInstall, version) 164 : await installNpmPlugin(toInstall, version)
163 165
164 name = fromDisk ? basename(toInstall) : toInstall 166 name = fromDisk ? basename(toInstall) : toInstall
165 const pluginType = name.startsWith('peertube-theme-') ? PluginType.THEME : PluginType.PLUGIN 167 const pluginType = PluginModel.getTypeFromNpmName(name)
166 const pluginName = this.normalizePluginName(name) 168 const pluginName = PluginModel.normalizePluginName(name)
167 169
168 const packageJSON = this.getPackageJSON(pluginName, pluginType) 170 const packageJSON = this.getPackageJSON(pluginName, pluginType)
169 if (!isPackageJSONValid(packageJSON, pluginType)) { 171 if (!isPackageJSONValid(packageJSON, pluginType)) {
@@ -173,6 +175,7 @@ export class PluginManager {
173 [ plugin ] = await PluginModel.upsert({ 175 [ plugin ] = await PluginModel.upsert({
174 name: pluginName, 176 name: pluginName,
175 description: packageJSON.description, 177 description: packageJSON.description,
178 homepage: packageJSON.homepage,
176 type: pluginType, 179 type: pluginType,
177 version: packageJSON.version, 180 version: packageJSON.version,
178 enabled: true, 181 enabled: true,
@@ -196,10 +199,10 @@ export class PluginManager {
196 await this.registerPluginOrTheme(plugin) 199 await this.registerPluginOrTheme(plugin)
197 } 200 }
198 201
199 async uninstall (packageName: string) { 202 async uninstall (npmName: string) {
200 logger.info('Uninstalling plugin %s.', packageName) 203 logger.info('Uninstalling plugin %s.', npmName)
201 204
202 const pluginName = this.normalizePluginName(packageName) 205 const pluginName = PluginModel.normalizePluginName(npmName)
203 206
204 try { 207 try {
205 await this.unregister(pluginName) 208 await this.unregister(pluginName)
@@ -207,9 +210,9 @@ export class PluginManager {
207 logger.warn('Cannot unregister plugin %s.', pluginName, { err }) 210 logger.warn('Cannot unregister plugin %s.', pluginName, { err })
208 } 211 }
209 212
210 const plugin = await PluginModel.load(pluginName) 213 const plugin = await PluginModel.loadByNpmName(npmName)
211 if (!plugin || plugin.uninstalled === true) { 214 if (!plugin || plugin.uninstalled === true) {
212 logger.error('Cannot uninstall plugin %s: it does not exist or is already uninstalled.', packageName) 215 logger.error('Cannot uninstall plugin %s: it does not exist or is already uninstalled.', npmName)
213 return 216 return
214 } 217 }
215 218
@@ -218,9 +221,9 @@ export class PluginManager {
218 221
219 await plugin.save() 222 await plugin.save()
220 223
221 await removeNpmPlugin(packageName) 224 await removeNpmPlugin(npmName)
222 225
223 logger.info('Plugin %s uninstalled.', packageName) 226 logger.info('Plugin %s uninstalled.', npmName)
224 } 227 }
225 228
226 // ###################### Private register ###################### 229 // ###################### Private register ######################
@@ -353,10 +356,6 @@ export class PluginManager {
353 return join(CONFIG.STORAGE.PLUGINS_DIR, 'node_modules', prefix + pluginName) 356 return join(CONFIG.STORAGE.PLUGINS_DIR, 'node_modules', prefix + pluginName)
354 } 357 }
355 358
356 private normalizePluginName (name: string) {
357 return name.replace(/^peertube-((theme)|(plugin))-/, '')
358 }
359
360 // ###################### Private getters ###################### 359 // ###################### Private getters ######################
361 360
362 private getRegisteredPluginsOrThemes (type: PluginType) { 361 private getRegisteredPluginsOrThemes (type: PluginType) {
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts
index 265ac7c17..a06add6b8 100644
--- a/server/middlewares/validators/plugins.ts
+++ b/server/middlewares/validators/plugins.ts
@@ -63,7 +63,7 @@ const uninstallPluginValidator = [
63 body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'), 63 body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
64 64
65 (req: express.Request, res: express.Response, next: express.NextFunction) => { 65 (req: express.Request, res: express.Response, next: express.NextFunction) => {
66 logger.debug('Checking managePluginValidator parameters', { parameters: req.body }) 66 logger.debug('Checking uninstallPluginValidator parameters', { parameters: req.body })
67 67
68 if (areValidationErrors(req, res)) return 68 if (areValidationErrors(req, res)) return
69 69
@@ -71,15 +71,15 @@ const uninstallPluginValidator = [
71 } 71 }
72] 72]
73 73
74const enabledPluginValidator = [ 74const existingPluginValidator = [
75 body('name').custom(isPluginNameValid).withMessage('Should have a valid plugin name'), 75 param('npmName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
76 76
77 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 77 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
78 logger.debug('Checking enabledPluginValidator parameters', { parameters: req.body }) 78 logger.debug('Checking enabledPluginValidator parameters', { parameters: req.params })
79 79
80 if (areValidationErrors(req, res)) return 80 if (areValidationErrors(req, res)) return
81 81
82 const plugin = await PluginModel.load(req.body.name) 82 const plugin = await PluginModel.loadByNpmName(req.params.npmName)
83 if (!plugin) { 83 if (!plugin) {
84 return res.status(404) 84 return res.status(404)
85 .json({ error: 'Plugin not found' }) 85 .json({ error: 'Plugin not found' })
@@ -110,7 +110,7 @@ export {
110 servePluginStaticDirectoryValidator, 110 servePluginStaticDirectoryValidator,
111 updatePluginSettingsValidator, 111 updatePluginSettingsValidator,
112 uninstallPluginValidator, 112 uninstallPluginValidator,
113 enabledPluginValidator, 113 existingPluginValidator,
114 installPluginValidator, 114 installPluginValidator,
115 listPluginsValidator 115 listPluginsValidator
116} 116}
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts
index 059a442de..60abaec65 100644
--- a/server/models/server/plugin.ts
+++ b/server/models/server/plugin.ts
@@ -1,7 +1,7 @@
1import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { getSort, throwIfNotValid } from '../utils' 2import { getSort, throwIfNotValid } from '../utils'
3import { 3import {
4 isPluginDescriptionValid, 4 isPluginDescriptionValid, isPluginHomepage,
5 isPluginNameValid, 5 isPluginNameValid,
6 isPluginTypeValid, 6 isPluginTypeValid,
7 isPluginVersionValid 7 isPluginVersionValid
@@ -20,7 +20,7 @@ import { FindAndCountOptions } from 'sequelize'
20 tableName: 'plugin', 20 tableName: 'plugin',
21 indexes: [ 21 indexes: [
22 { 22 {
23 fields: [ 'name' ], 23 fields: [ 'name', 'type' ],
24 unique: true 24 unique: true
25 } 25 }
26 ] 26 ]
@@ -59,6 +59,11 @@ export class PluginModel extends Model<PluginModel> {
59 @Column 59 @Column
60 description: string 60 description: string
61 61
62 @AllowNull(false)
63 @Is('PluginHomepage', value => throwIfNotValid(value, isPluginHomepage, 'homepage'))
64 @Column
65 homepage: string
66
62 @AllowNull(true) 67 @AllowNull(true)
63 @Column(DataType.JSONB) 68 @Column(DataType.JSONB)
64 settings: any 69 settings: any
@@ -84,10 +89,14 @@ export class PluginModel extends Model<PluginModel> {
84 return PluginModel.findAll(query) 89 return PluginModel.findAll(query)
85 } 90 }
86 91
87 static load (pluginName: string) { 92 static loadByNpmName (npmName: string) {
93 const name = this.normalizePluginName(npmName)
94 const type = this.getTypeFromNpmName(npmName)
95
88 const query = { 96 const query = {
89 where: { 97 where: {
90 name: pluginName 98 name,
99 type
91 } 100 }
92 } 101 }
93 102
@@ -150,6 +159,16 @@ export class PluginModel extends Model<PluginModel> {
150 }) 159 })
151 } 160 }
152 161
162 static normalizePluginName (name: string) {
163 return name.replace(/^peertube-((theme)|(plugin))-/, '')
164 }
165
166 static getTypeFromNpmName (npmName: string) {
167 return npmName.startsWith('peertube-plugin-')
168 ? PluginType.PLUGIN
169 : PluginType.THEME
170 }
171
153 toFormattedJSON (): PeerTubePlugin { 172 toFormattedJSON (): PeerTubePlugin {
154 return { 173 return {
155 name: this.name, 174 name: this.name,
@@ -159,6 +178,7 @@ export class PluginModel extends Model<PluginModel> {
159 uninstalled: this.uninstalled, 178 uninstalled: this.uninstalled,
160 peertubeEngine: this.peertubeEngine, 179 peertubeEngine: this.peertubeEngine,
161 description: this.description, 180 description: this.description,
181 homepage: this.homepage,
162 settings: this.settings, 182 settings: this.settings,
163 createdAt: this.createdAt, 183 createdAt: this.createdAt,
164 updatedAt: this.updatedAt 184 updatedAt: this.updatedAt