WIP plugins: plugin settings on server side
authorChocobozzz <me@florianbigard.com>
Wed, 10 Jul 2019 14:59:53 +0000 (16:59 +0200)
committerChocobozzz <chocobozzz@cpy.re>
Wed, 24 Jul 2019 08:58:16 +0000 (10:58 +0200)
21 files changed:
server/controllers/api/index.ts
server/controllers/api/plugins.ts [new file with mode: 0644]
server/helpers/custom-validators/video-channels.ts
server/initializers/constants.ts
server/lib/plugins/plugin-manager.ts
server/lib/plugins/yarn.ts
server/middlewares/validators/plugins.ts
server/middlewares/validators/sort.ts
server/models/server/plugin.ts
server/typings/express.ts
shared/models/plugins/install-plugin.model.ts [new file with mode: 0644]
shared/models/plugins/manage-plugin.model.ts [new file with mode: 0644]
shared/models/plugins/peertube-plugin.model.ts [new file with mode: 0644]
shared/models/plugins/plugin-library.model.ts
shared/models/plugins/plugin-settings-manager.model.ts [new file with mode: 0644]
shared/models/plugins/register-hook.model.ts [new file with mode: 0644]
shared/models/plugins/register-options.model.ts [new file with mode: 0644]
shared/models/plugins/register-options.type.ts [deleted file]
shared/models/plugins/register-setting.model.ts [new file with mode: 0644]
shared/models/plugins/register.model.ts [deleted file]
shared/models/users/user-right.enum.ts

index ea2615e2803db65195e0a511b52da301e378a0cc..0876283a2022436aff4fa95ec0db666ac2a9cf11 100644 (file)
@@ -14,6 +14,7 @@ import { searchRouter } from './search'
 import { overviewsRouter } from './overviews'
 import { videoPlaylistRouter } from './video-playlist'
 import { CONFIG } from '../../initializers/config'
+import { pluginsRouter } from '../plugins'
 
 const apiRouter = express.Router()
 
@@ -42,6 +43,7 @@ apiRouter.use('/videos', videosRouter)
 apiRouter.use('/jobs', jobsRouter)
 apiRouter.use('/search', searchRouter)
 apiRouter.use('/overviews', overviewsRouter)
+apiRouter.use('/plugins', pluginsRouter)
 apiRouter.use('/ping', pong)
 apiRouter.use('/*', badRequest)
 
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts
new file mode 100644 (file)
index 0000000..89cc67f
--- /dev/null
@@ -0,0 +1,121 @@
+import * as express from 'express'
+import { getFormattedObjects } from '../../helpers/utils'
+import {
+  asyncMiddleware,
+  authenticate,
+  ensureUserHasRight,
+  paginationValidator,
+  setDefaultPagination,
+  setDefaultSort
+} from '../../middlewares'
+import { pluginsSortValidator } from '../../middlewares/validators'
+import { PluginModel } from '../../models/server/plugin'
+import { UserRight } from '../../../shared/models/users'
+import {
+  enabledPluginValidator,
+  installPluginValidator,
+  listPluginsValidator,
+  uninstallPluginValidator,
+  updatePluginSettingsValidator
+} from '../../middlewares/validators/plugins'
+import { PluginManager } from '../../lib/plugins/plugin-manager'
+import { InstallPlugin } from '../../../shared/models/plugins/install-plugin.model'
+import { ManagePlugin } from '../../../shared/models/plugins/manage-plugin.model'
+
+const pluginRouter = express.Router()
+
+pluginRouter.get('/',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_PLUGINS),
+  listPluginsValidator,
+  paginationValidator,
+  pluginsSortValidator,
+  setDefaultSort,
+  setDefaultPagination,
+  asyncMiddleware(listPlugins)
+)
+
+pluginRouter.get('/:pluginName/settings',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_PLUGINS),
+  asyncMiddleware(enabledPluginValidator),
+  asyncMiddleware(listPluginSettings)
+)
+
+pluginRouter.put('/:pluginName/settings',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_PLUGINS),
+  updatePluginSettingsValidator,
+  asyncMiddleware(enabledPluginValidator),
+  asyncMiddleware(updatePluginSettings)
+)
+
+pluginRouter.post('/install',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_PLUGINS),
+  installPluginValidator,
+  asyncMiddleware(installPlugin)
+)
+
+pluginRouter.post('/uninstall',
+  authenticate,
+  ensureUserHasRight(UserRight.MANAGE_PLUGINS),
+  uninstallPluginValidator,
+  asyncMiddleware(uninstallPlugin)
+)
+
+// ---------------------------------------------------------------------------
+
+export {
+  pluginRouter
+}
+
+// ---------------------------------------------------------------------------
+
+async function listPlugins (req: express.Request, res: express.Response) {
+  const type = req.query.type
+
+  const resultList = await PluginModel.listForApi({
+    type,
+    start: req.query.start,
+    count: req.query.count,
+    sort: req.query.sort
+  })
+
+  return res.json(getFormattedObjects(resultList.data, resultList.total))
+}
+
+async function installPlugin (req: express.Request, res: express.Response) {
+  const body: InstallPlugin = req.body
+
+  await PluginManager.Instance.install(body.npmName)
+
+  return res.sendStatus(204)
+}
+
+async function uninstallPlugin (req: express.Request, res: express.Response) {
+  const body: ManagePlugin = req.body
+
+  await PluginManager.Instance.uninstall(body.npmName)
+
+  return res.sendStatus(204)
+}
+
+async function listPluginSettings (req: express.Request, res: express.Response) {
+  const plugin = res.locals.plugin
+
+  const settings = await PluginManager.Instance.getSettings(plugin.name)
+
+  return res.json({
+    settings
+  })
+}
+
+async function updatePluginSettings (req: express.Request, res: express.Response) {
+  const plugin = res.locals.plugin
+
+  plugin.settings = req.body.settings
+  await plugin.save()
+
+  return res.sendStatus(204)
+}
index f818ce8f1acc2052b2a30eb50c586a8eeab818dd..e1a2f9503366a64e55ffbd93275305ce27e3d887 100644 (file)
@@ -51,9 +51,7 @@ export {
 
 function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
   if (!videoChannel) {
-    res.status(404)
-       .json({ error: 'Video channel not found' })
-       .end()
+    ``
 
     return false
   }
index e5f88b71d7bb0d91f7185cffc2604e442077d190..4163fe49da7a14b689053b220003ce873a3d6bbf 100644 (file)
@@ -62,7 +62,9 @@ const SORTABLE_COLUMNS = {
 
   USER_NOTIFICATIONS: [ 'createdAt' ],
 
-  VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ]
+  VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ],
+
+  PLUGINS: [ 'name', 'createdAt', 'updatedAt' ]
 }
 
 const OAUTH_LIFETIME = {
index 8496979f8b203c412e7103c5bc60928aee171471..3d8375acdf01c6136d87105c341cf647c40e4304 100644 (file)
@@ -1,6 +1,5 @@
 import { PluginModel } from '../../models/server/plugin'
 import { logger } from '../../helpers/logger'
-import { RegisterHookOptions } from '../../../shared/models/plugins/register.model'
 import { basename, join } from 'path'
 import { CONFIG } from '../../initializers/config'
 import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins'
@@ -11,7 +10,9 @@ import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants'
 import { PluginType } from '../../../shared/models/plugins/plugin.type'
 import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
 import { outputFile } from 'fs-extra'
-import { ServerConfigPlugin } from '../../../shared/models/server'
+import { RegisterSettingOptions } from '../../../shared/models/plugins/register-setting.model'
+import { RegisterHookOptions } from '../../../shared/models/plugins/register-hook.model'
+import { PluginSettingsManager } from '../../../shared/models/plugins/plugin-settings-manager.model'
 
 export interface RegisteredPlugin {
   name: string
@@ -43,26 +44,13 @@ export class PluginManager {
   private static instance: PluginManager
 
   private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {}
+  private settings: { [ name: string ]: RegisterSettingOptions[] } = {}
   private hooks: { [ name: string ]: HookInformationValue[] } = {}
 
   private constructor () {
   }
 
-  async registerPluginsAndThemes () {
-    await this.resetCSSGlobalFile()
-
-    const plugins = await PluginModel.listEnabledPluginsAndThemes()
-
-    for (const plugin of plugins) {
-      try {
-        await this.registerPluginOrTheme(plugin)
-      } catch (err) {
-        logger.error('Cannot register plugin %s, skipping.', plugin.name, { err })
-      }
-    }
-
-    this.sortHooksByPriority()
-  }
+  // ###################### Getters ######################
 
   getRegisteredPluginOrTheme (name: string) {
     return this.registeredPlugins[name]
@@ -92,6 +80,12 @@ export class PluginManager {
     return this.getRegisteredPluginsOrThemes(PluginType.THEME)
   }
 
+  getSettings (name: string) {
+    return this.settings[name] || []
+  }
+
+  // ###################### Hooks ######################
+
   async runHook (hookName: string, param?: any) {
     let result = param
 
@@ -99,8 +93,11 @@ export class PluginManager {
 
     for (const hook of this.hooks[hookName]) {
       try {
-        if (wait) result = await hook.handler(param)
-        else result = hook.handler()
+        if (wait) {
+          result = await hook.handler(param)
+        } else {
+          result = hook.handler()
+        }
       } catch (err) {
         logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
       }
@@ -109,6 +106,24 @@ export class PluginManager {
     return result
   }
 
+  // ###################### Registration ######################
+
+  async registerPluginsAndThemes () {
+    await this.resetCSSGlobalFile()
+
+    const plugins = await PluginModel.listEnabledPluginsAndThemes()
+
+    for (const plugin of plugins) {
+      try {
+        await this.registerPluginOrTheme(plugin)
+      } catch (err) {
+        logger.error('Cannot register plugin %s, skipping.', plugin.name, { err })
+      }
+    }
+
+    this.sortHooksByPriority()
+  }
+
   async unregister (name: string) {
     const plugin = this.getRegisteredPlugin(name)
 
@@ -133,7 +148,9 @@ export class PluginManager {
     await this.regeneratePluginGlobalCSS()
   }
 
-  async install (toInstall: string, version: string, fromDisk = false) {
+  // ###################### Installation ######################
+
+  async install (toInstall: string, version?: string, fromDisk = false) {
     let plugin: PluginModel
     let name: string
 
@@ -206,6 +223,8 @@ export class PluginManager {
     logger.info('Plugin %s uninstalled.', packageName)
   }
 
+  // ###################### Private register ######################
+
   private async registerPluginOrTheme (plugin: PluginModel) {
     logger.info('Registering plugin or theme %s.', plugin.name)
 
@@ -251,13 +270,25 @@ export class PluginManager {
       })
     }
 
+    const registerSetting = (options: RegisterSettingOptions) => {
+      if (!this.settings[plugin.name]) this.settings[plugin.name] = []
+
+      this.settings[plugin.name].push(options)
+    }
+
+    const settingsManager: PluginSettingsManager = {
+      getSetting: (name: string) => PluginModel.getSetting(plugin.name, name),
+
+      setSetting: (name: string, value: string) => PluginModel.setSetting(plugin.name, name, value)
+    }
+
     const library: PluginLibrary = require(join(pluginPath, packageJSON.library))
 
     if (!isLibraryCodeValid(library)) {
       throw new Error('Library code is not valid (miss register or unregister function)')
     }
 
-    library.register({ registerHook })
+    library.register({ registerHook, registerSetting, settingsManager })
 
     logger.info('Add plugin %s CSS to global file.', plugin.name)
 
@@ -266,13 +297,7 @@ export class PluginManager {
     return library
   }
 
-  private sortHooksByPriority () {
-    for (const hookName of Object.keys(this.hooks)) {
-      this.hooks[hookName].sort((a, b) => {
-        return b.priority - a.priority
-      })
-    }
-  }
+  // ###################### CSS ######################
 
   private resetCSSGlobalFile () {
     return outputFile(PLUGIN_GLOBAL_CSS_PATH, '')
@@ -296,6 +321,26 @@ export class PluginManager {
     })
   }
 
+  private async regeneratePluginGlobalCSS () {
+    await this.resetCSSGlobalFile()
+
+    for (const key of Object.keys(this.registeredPlugins)) {
+      const plugin = this.registeredPlugins[key]
+
+      await this.addCSSToGlobalFile(plugin.path, plugin.css)
+    }
+  }
+
+  // ###################### Utils ######################
+
+  private sortHooksByPriority () {
+    for (const hookName of Object.keys(this.hooks)) {
+      this.hooks[hookName].sort((a, b) => {
+        return b.priority - a.priority
+      })
+    }
+  }
+
   private getPackageJSON (pluginName: string, pluginType: PluginType) {
     const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json')
 
@@ -312,15 +357,7 @@ export class PluginManager {
     return name.replace(/^peertube-((theme)|(plugin))-/, '')
   }
 
-  private async regeneratePluginGlobalCSS () {
-    await this.resetCSSGlobalFile()
-
-    for (const key of Object.keys(this.registeredPlugins)) {
-      const plugin = this.registeredPlugins[key]
-
-      await this.addCSSToGlobalFile(plugin.path, plugin.css)
-    }
-  }
+  // ###################### Private getters ######################
 
   private getRegisteredPluginsOrThemes (type: PluginType) {
     const plugins: RegisteredPlugin[] = []
index 35fe1625f4808001f628456759f05d6c8225d751..5fe1c50463d0f30aafd9d567f7a186a9896a6a1b 100644 (file)
@@ -5,12 +5,14 @@ import { CONFIG } from '../../initializers/config'
 import { outputJSON, pathExists } from 'fs-extra'
 import { join } from 'path'
 
-async function installNpmPlugin (name: string, version: string) {
+async function installNpmPlugin (name: string, version?: string) {
   // Security check
   checkNpmPluginNameOrThrow(name)
-  checkPluginVersionOrThrow(version)
+  if (version) checkPluginVersionOrThrow(version)
+
+  let toInstall = name
+  if (version) toInstall += `@${version}`
 
-  const toInstall = `${name}@${version}`
   await execYarn('add ' + toInstall)
 }
 
index fcb461624450729ddaedb28cffc12baec228e801..265ac7c17b0f84f1868328ad29b8e1897a8591f9 100644 (file)
@@ -1,10 +1,11 @@
 import * as express from 'express'
-import { param } from 'express-validator/check'
+import { param, query, body } from 'express-validator/check'
 import { logger } from '../../helpers/logger'
 import { areValidationErrors } from './utils'
-import { isPluginNameValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins'
+import { isPluginNameValid, isPluginTypeValid, isPluginVersionValid, isNpmPluginNameValid } from '../../helpers/custom-validators/plugins'
 import { PluginManager } from '../../lib/plugins/plugin-manager'
-import { isSafePath } from '../../helpers/custom-validators/misc'
+import { isBooleanValid, isSafePath } from '../../helpers/custom-validators/misc'
+import { PluginModel } from '../../models/server/plugin'
 
 const servePluginStaticDirectoryValidator = [
   param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
@@ -28,8 +29,88 @@ const servePluginStaticDirectoryValidator = [
   }
 ]
 
+const listPluginsValidator = [
+  query('type')
+    .optional()
+    .custom(isPluginTypeValid).withMessage('Should have a valid plugin type'),
+  query('uninstalled')
+    .optional()
+    .toBoolean()
+    .custom(isBooleanValid).withMessage('Should have a valid uninstalled attribute'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking listPluginsValidator parameters', { parameters: req.query })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
+const installPluginValidator = [
+  body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking installPluginValidator parameters', { parameters: req.body })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
+const uninstallPluginValidator = [
+  body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking managePluginValidator parameters', { parameters: req.body })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
+const enabledPluginValidator = [
+  body('name').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
+
+  async (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking enabledPluginValidator parameters', { parameters: req.body })
+
+    if (areValidationErrors(req, res)) return
+
+    const plugin = await PluginModel.load(req.body.name)
+    if (!plugin) {
+      return res.status(404)
+         .json({ error: 'Plugin not found' })
+         .end()
+    }
+
+    res.locals.plugin = plugin
+
+    return next()
+  }
+]
+
+const updatePluginSettingsValidator = [
+  body('settings').exists().withMessage('Should have settings'),
+
+  (req: express.Request, res: express.Response, next: express.NextFunction) => {
+    logger.debug('Checking enabledPluginValidator parameters', { parameters: req.body })
+
+    if (areValidationErrors(req, res)) return
+
+    return next()
+  }
+]
+
 // ---------------------------------------------------------------------------
 
 export {
-  servePluginStaticDirectoryValidator
+  servePluginStaticDirectoryValidator,
+  updatePluginSettingsValidator,
+  uninstallPluginValidator,
+  enabledPluginValidator,
+  installPluginValidator,
+  listPluginsValidator
 }
index b497798d129c1e782567213c93dcb3a9b22bf5e0..102db85cbd5156c90c701f9ba043853934be9584 100644 (file)
@@ -21,6 +21,7 @@ const SORTABLE_ACCOUNTS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUM
 const SORTABLE_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST)
 const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS)
 const SORTABLE_VIDEO_PLAYLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_PLAYLISTS)
+const SORTABLE_PLUGINS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.PLUGINS)
 
 const usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
 const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
@@ -41,6 +42,7 @@ const accountsBlocklistSortValidator = checkSort(SORTABLE_ACCOUNTS_BLOCKLIST_COL
 const serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS)
 const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS)
 const videoPlaylistsSortValidator = checkSort(SORTABLE_VIDEO_PLAYLISTS_COLUMNS)
+const pluginsSortValidator = checkSort(SORTABLE_PLUGINS_COLUMNS)
 
 // ---------------------------------------------------------------------------
 
@@ -63,5 +65,6 @@ export {
   accountsBlocklistSortValidator,
   serversBlocklistSortValidator,
   userNotificationsSortValidator,
-  videoPlaylistsSortValidator
+  videoPlaylistsSortValidator,
+  pluginsSortValidator
 }
index b3b8276dffe87b7fa15b97ae7953fa1fa887cad8..059a442de5dfe9a96d9206f0b8698d65708aa9d0 100644 (file)
@@ -1,11 +1,20 @@
-import { AllowNull, Column, CreatedAt, DataType, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
-import { throwIfNotValid } from '../utils'
+import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { getSort, throwIfNotValid } from '../utils'
 import {
   isPluginDescriptionValid,
   isPluginNameValid,
   isPluginTypeValid,
   isPluginVersionValid
 } from '../../helpers/custom-validators/plugins'
+import { PluginType } from '../../../shared/models/plugins/plugin.type'
+import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model'
+import { FindAndCountOptions } from 'sequelize'
+
+@DefaultScope(() => ({
+  attributes: {
+    exclude: [ 'storage' ]
+  }
+}))
 
 @Table({
   tableName: 'plugin',
@@ -85,14 +94,75 @@ export class PluginModel extends Model<PluginModel> {
     return PluginModel.findOne(query)
   }
 
-  static uninstall (pluginName: string) {
+  static getSetting (pluginName: string, settingName: string) {
+    const query = {
+      attributes: [ 'settings' ],
+      where: {
+        name: pluginName
+      }
+    }
+
+    return PluginModel.findOne(query)
+      .then(p => p.settings)
+      .then(settings => {
+        if (!settings) return undefined
+
+        return settings[settingName]
+      })
+  }
+
+  static setSetting (pluginName: string, settingName: string, settingValue: string) {
     const query = {
       where: {
         name: pluginName
       }
     }
 
-    return PluginModel.update({ enabled: false, uninstalled: true }, query)
+    const toSave = {
+      [`settings.${settingName}`]: settingValue
+    }
+
+    return PluginModel.update(toSave, query)
+      .then(() => undefined)
+  }
+
+  static listForApi (options: {
+    type?: PluginType,
+    uninstalled?: boolean,
+    start: number,
+    count: number,
+    sort: string
+  }) {
+    const query: FindAndCountOptions = {
+      offset: options.start,
+      limit: options.count,
+      order: getSort(options.sort),
+      where: {}
+    }
+
+    if (options.type) query.where['type'] = options.type
+    if (options.uninstalled) query.where['uninstalled'] = options.uninstalled
+
+    return PluginModel
+      .findAndCountAll(query)
+      .then(({ rows, count }) => {
+        return { total: count, data: rows }
+      })
+  }
+
+  toFormattedJSON (): PeerTubePlugin {
+    return {
+      name: this.name,
+      type: this.type,
+      version: this.version,
+      enabled: this.enabled,
+      uninstalled: this.uninstalled,
+      peertubeEngine: this.peertubeEngine,
+      description: this.description,
+      settings: this.settings,
+      createdAt: this.createdAt,
+      updatedAt: this.updatedAt
+    }
   }
 
 }
index aec10b6065fa9f8e68a69e4f18cafdb3ad7fe94d..3bffc1e9a589631190c157ce11c22249f99de023 100644 (file)
@@ -21,10 +21,10 @@ import { VideoBlacklistModel } from '../models/video/video-blacklist'
 import { VideoCaptionModel } from '../models/video/video-caption'
 import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
 import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
+import { PluginModel } from '../models/server/plugin'
 
 declare module 'express' {
 
-
   interface Response {
     locals: {
       video?: VideoModel
@@ -81,6 +81,8 @@ declare module 'express' {
       authenticated?: boolean
 
       registeredPlugin?: RegisteredPlugin
+
+      plugin?: PluginModel
     }
   }
 }
diff --git a/shared/models/plugins/install-plugin.model.ts b/shared/models/plugins/install-plugin.model.ts
new file mode 100644 (file)
index 0000000..03d87fe
--- /dev/null
@@ -0,0 +1,3 @@
+export interface InstallPlugin {
+  npmName: string
+}
diff --git a/shared/models/plugins/manage-plugin.model.ts b/shared/models/plugins/manage-plugin.model.ts
new file mode 100644 (file)
index 0000000..612b305
--- /dev/null
@@ -0,0 +1,3 @@
+export interface ManagePlugin {
+  npmName: string
+}
diff --git a/shared/models/plugins/peertube-plugin.model.ts b/shared/models/plugins/peertube-plugin.model.ts
new file mode 100644 (file)
index 0000000..2a1dfb3
--- /dev/null
@@ -0,0 +1,12 @@
+export interface PeerTubePlugin {
+  name: string
+  type: number
+  version: string
+  enabled: boolean
+  uninstalled: boolean
+  peertubeEngine: string
+  description: string
+  settings: any
+  createdAt: Date
+  updatedAt: Date
+}
index 8eb18d7206511fc819e27547d6929275e2fa0514..df6499b6b7de32fb800eeaae3060516db1984258 100644 (file)
@@ -1,6 +1,7 @@
-import { RegisterOptions } from './register-options.type'
+import { RegisterOptions } from './register-options.model'
 
 export interface PluginLibrary {
   register: (options: RegisterOptions) => void
+
   unregister: () => Promise<any>
 }
diff --git a/shared/models/plugins/plugin-settings-manager.model.ts b/shared/models/plugins/plugin-settings-manager.model.ts
new file mode 100644 (file)
index 0000000..63390a1
--- /dev/null
@@ -0,0 +1,7 @@
+import * as Bluebird from 'bluebird'
+
+export interface PluginSettingsManager {
+  getSetting: (name: string) => Bluebird<string>
+
+  setSetting: (name: string, value: string) => Bluebird<any>
+}
diff --git a/shared/models/plugins/register-hook.model.ts b/shared/models/plugins/register-hook.model.ts
new file mode 100644 (file)
index 0000000..0ed2157
--- /dev/null
@@ -0,0 +1,5 @@
+export interface RegisterHookOptions {
+  target: string
+  handler: Function
+  priority?: number
+}
diff --git a/shared/models/plugins/register-options.model.ts b/shared/models/plugins/register-options.model.ts
new file mode 100644 (file)
index 0000000..e60ce3f
--- /dev/null
@@ -0,0 +1,11 @@
+import { RegisterHookOptions } from './register-hook.model'
+import { RegisterSettingOptions } from './register-setting.model'
+import { PluginSettingsManager } from './plugin-settings-manager.model'
+
+export type RegisterOptions = {
+  registerHook: (options: RegisterHookOptions) => void
+
+  registerSetting: (options: RegisterSettingOptions) => void
+
+  settingsManager: PluginSettingsManager
+}
diff --git a/shared/models/plugins/register-options.type.ts b/shared/models/plugins/register-options.type.ts
deleted file mode 100644 (file)
index a074f39..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-import { RegisterHookOptions } from './register.model'
-
-export type RegisterOptions = {
-  registerHook: (options: RegisterHookOptions) => void
-}
diff --git a/shared/models/plugins/register-setting.model.ts b/shared/models/plugins/register-setting.model.ts
new file mode 100644 (file)
index 0000000..e7af75d
--- /dev/null
@@ -0,0 +1,6 @@
+export interface RegisterSettingOptions {
+  name: string
+  label: string
+  type: 'input'
+  default?: string
+}
diff --git a/shared/models/plugins/register.model.ts b/shared/models/plugins/register.model.ts
deleted file mode 100644 (file)
index 0ed2157..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface RegisterHookOptions {
-  target: string
-  handler: Function
-  priority?: number
-}
index 71701bdb480a92e932abed05602f7dac4ae35505..4a28a229d21fc429b5ebcf4af2870528df0e2ff3 100644 (file)
@@ -31,5 +31,7 @@ export enum UserRight {
   UPDATE_ANY_VIDEO_PLAYLIST,
 
   SEE_ALL_VIDEOS,
-  CHANGE_VIDEO_OWNERSHIP
+  CHANGE_VIDEO_OWNERSHIP,
+
+  MANAGE_PLUGINS
 }