From: Chocobozzz Date: Wed, 10 Jul 2019 14:59:53 +0000 (+0200) Subject: WIP plugins: plugin settings on server side X-Git-Tag: v1.4.0-rc.1~119 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=ad91e7006e41f8ee5b8dcefee30f99e8ca44133a;p=oweals%2Fpeertube.git WIP plugins: plugin settings on server side --- diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts index ea2615e28..0876283a2 100644 --- a/server/controllers/api/index.ts +++ b/server/controllers/api/index.ts @@ -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 index 000000000..89cc67f54 --- /dev/null +++ b/server/controllers/api/plugins.ts @@ -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) +} diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts index f818ce8f1..e1a2f9503 100644 --- a/server/helpers/custom-validators/video-channels.ts +++ b/server/helpers/custom-validators/video-channels.ts @@ -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 } diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index e5f88b71d..4163fe49d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -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 = { 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 @@ 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[] = [] diff --git a/server/lib/plugins/yarn.ts b/server/lib/plugins/yarn.ts index 35fe1625f..5fe1c5046 100644 --- a/server/lib/plugins/yarn.ts +++ b/server/lib/plugins/yarn.ts @@ -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) } diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index fcb461624..265ac7c17 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts @@ -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 } diff --git a/server/middlewares/validators/sort.ts b/server/middlewares/validators/sort.ts index b497798d1..102db85cb 100644 --- a/server/middlewares/validators/sort.ts +++ b/server/middlewares/validators/sort.ts @@ -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 } diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index b3b8276df..059a442de 100644 --- a/server/models/server/plugin.ts +++ b/server/models/server/plugin.ts @@ -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 { 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 + } } } diff --git a/server/typings/express.ts b/server/typings/express.ts index aec10b606..3bffc1e9a 100644 --- a/server/typings/express.ts +++ b/server/typings/express.ts @@ -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 index 000000000..03d87fe57 --- /dev/null +++ b/shared/models/plugins/install-plugin.model.ts @@ -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 index 000000000..612b3056c --- /dev/null +++ b/shared/models/plugins/manage-plugin.model.ts @@ -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 index 000000000..2a1dfb3a7 --- /dev/null +++ b/shared/models/plugins/peertube-plugin.model.ts @@ -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 +} diff --git a/shared/models/plugins/plugin-library.model.ts b/shared/models/plugins/plugin-library.model.ts index 8eb18d720..df6499b6b 100644 --- a/shared/models/plugins/plugin-library.model.ts +++ b/shared/models/plugins/plugin-library.model.ts @@ -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 } diff --git a/shared/models/plugins/plugin-settings-manager.model.ts b/shared/models/plugins/plugin-settings-manager.model.ts new file mode 100644 index 000000000..63390a190 --- /dev/null +++ b/shared/models/plugins/plugin-settings-manager.model.ts @@ -0,0 +1,7 @@ +import * as Bluebird from 'bluebird' + +export interface PluginSettingsManager { + getSetting: (name: string) => Bluebird + + setSetting: (name: string, value: string) => Bluebird +} diff --git a/shared/models/plugins/register-hook.model.ts b/shared/models/plugins/register-hook.model.ts new file mode 100644 index 000000000..0ed2157bd --- /dev/null +++ b/shared/models/plugins/register-hook.model.ts @@ -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 index 000000000..e60ce3fe0 --- /dev/null +++ b/shared/models/plugins/register-options.model.ts @@ -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 index a074f3931..000000000 --- a/shared/models/plugins/register-options.type.ts +++ /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 index 000000000..e7af75dca --- /dev/null +++ b/shared/models/plugins/register-setting.model.ts @@ -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 index 0ed2157bd..000000000 --- a/shared/models/plugins/register.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface RegisterHookOptions { - target: string - handler: Function - priority?: number -} diff --git a/shared/models/users/user-right.enum.ts b/shared/models/users/user-right.enum.ts index 71701bdb4..4a28a229d 100644 --- a/shared/models/users/user-right.enum.ts +++ b/shared/models/users/user-right.enum.ts @@ -31,5 +31,7 @@ export enum UserRight { UPDATE_ANY_VIDEO_PLAYLIST, SEE_ALL_VIDEOS, - CHANGE_VIDEO_OWNERSHIP + CHANGE_VIDEO_OWNERSHIP, + + MANAGE_PLUGINS }