WIP plugins: plugin settings on server side

This commit is contained in:
Chocobozzz 2019-07-10 16:59:53 +02:00 committed by Chocobozzz
parent ffb321bedc
commit ad91e7006e
20 changed files with 419 additions and 61 deletions

View File

@ -14,6 +14,7 @@ import { searchRouter } from './search'
import { overviewsRouter } from './overviews' import { overviewsRouter } from './overviews'
import { videoPlaylistRouter } from './video-playlist' import { videoPlaylistRouter } from './video-playlist'
import { CONFIG } from '../../initializers/config' import { CONFIG } from '../../initializers/config'
import { pluginsRouter } from '../plugins'
const apiRouter = express.Router() const apiRouter = express.Router()
@ -42,6 +43,7 @@ apiRouter.use('/videos', videosRouter)
apiRouter.use('/jobs', jobsRouter) apiRouter.use('/jobs', jobsRouter)
apiRouter.use('/search', searchRouter) apiRouter.use('/search', searchRouter)
apiRouter.use('/overviews', overviewsRouter) apiRouter.use('/overviews', overviewsRouter)
apiRouter.use('/plugins', pluginsRouter)
apiRouter.use('/ping', pong) apiRouter.use('/ping', pong)
apiRouter.use('/*', badRequest) apiRouter.use('/*', badRequest)

View File

@ -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)
}

View File

@ -51,9 +51,7 @@ export {
function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
if (!videoChannel) { if (!videoChannel) {
res.status(404) ``
.json({ error: 'Video channel not found' })
.end()
return false return false
} }

View File

@ -62,7 +62,9 @@ const SORTABLE_COLUMNS = {
USER_NOTIFICATIONS: [ 'createdAt' ], USER_NOTIFICATIONS: [ 'createdAt' ],
VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ] VIDEO_PLAYLISTS: [ 'displayName', 'createdAt', 'updatedAt' ],
PLUGINS: [ 'name', 'createdAt', 'updatedAt' ]
} }
const OAUTH_LIFETIME = { const OAUTH_LIFETIME = {

View File

@ -1,6 +1,5 @@
import { PluginModel } from '../../models/server/plugin' import { PluginModel } from '../../models/server/plugin'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { RegisterHookOptions } from '../../../shared/models/plugins/register.model'
import { basename, join } from 'path' import { basename, join } from 'path'
import { CONFIG } from '../../initializers/config' import { CONFIG } from '../../initializers/config'
import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' 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 { PluginType } from '../../../shared/models/plugins/plugin.type'
import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
import { outputFile } from 'fs-extra' 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 { export interface RegisteredPlugin {
name: string name: string
@ -43,26 +44,13 @@ export class PluginManager {
private static instance: PluginManager private static instance: PluginManager
private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {} private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {}
private settings: { [ name: string ]: RegisterSettingOptions[] } = {}
private hooks: { [ name: string ]: HookInformationValue[] } = {} private hooks: { [ name: string ]: HookInformationValue[] } = {}
private constructor () { private constructor () {
} }
async registerPluginsAndThemes () { // ###################### Getters ######################
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()
}
getRegisteredPluginOrTheme (name: string) { getRegisteredPluginOrTheme (name: string) {
return this.registeredPlugins[name] return this.registeredPlugins[name]
@ -92,6 +80,12 @@ export class PluginManager {
return this.getRegisteredPluginsOrThemes(PluginType.THEME) return this.getRegisteredPluginsOrThemes(PluginType.THEME)
} }
getSettings (name: string) {
return this.settings[name] || []
}
// ###################### Hooks ######################
async runHook (hookName: string, param?: any) { async runHook (hookName: string, param?: any) {
let result = param let result = param
@ -99,8 +93,11 @@ export class PluginManager {
for (const hook of this.hooks[hookName]) { for (const hook of this.hooks[hookName]) {
try { try {
if (wait) result = await hook.handler(param) if (wait) {
else result = hook.handler() result = await hook.handler(param)
} else {
result = hook.handler()
}
} catch (err) { } catch (err) {
logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err }) logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
} }
@ -109,6 +106,24 @@ export class PluginManager {
return result 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) { async unregister (name: string) {
const plugin = this.getRegisteredPlugin(name) const plugin = this.getRegisteredPlugin(name)
@ -133,7 +148,9 @@ export class PluginManager {
await this.regeneratePluginGlobalCSS() await this.regeneratePluginGlobalCSS()
} }
async install (toInstall: string, version: string, fromDisk = false) { // ###################### Installation ######################
async install (toInstall: string, version?: string, fromDisk = false) {
let plugin: PluginModel let plugin: PluginModel
let name: string let name: string
@ -206,6 +223,8 @@ export class PluginManager {
logger.info('Plugin %s uninstalled.', packageName) logger.info('Plugin %s uninstalled.', packageName)
} }
// ###################### Private register ######################
private async registerPluginOrTheme (plugin: PluginModel) { private async registerPluginOrTheme (plugin: PluginModel) {
logger.info('Registering plugin or theme %s.', plugin.name) 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)) const library: PluginLibrary = require(join(pluginPath, packageJSON.library))
if (!isLibraryCodeValid(library)) { if (!isLibraryCodeValid(library)) {
throw new Error('Library code is not valid (miss register or unregister function)') 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) logger.info('Add plugin %s CSS to global file.', plugin.name)
@ -266,13 +297,7 @@ export class PluginManager {
return library return library
} }
private sortHooksByPriority () { // ###################### CSS ######################
for (const hookName of Object.keys(this.hooks)) {
this.hooks[hookName].sort((a, b) => {
return b.priority - a.priority
})
}
}
private resetCSSGlobalFile () { private resetCSSGlobalFile () {
return outputFile(PLUGIN_GLOBAL_CSS_PATH, '') 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) { private getPackageJSON (pluginName: string, pluginType: PluginType) {
const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json') const pluginPath = join(this.getPluginPath(pluginName, pluginType), 'package.json')
@ -312,15 +357,7 @@ export class PluginManager {
return name.replace(/^peertube-((theme)|(plugin))-/, '') return name.replace(/^peertube-((theme)|(plugin))-/, '')
} }
private async regeneratePluginGlobalCSS () { // ###################### Private getters ######################
await this.resetCSSGlobalFile()
for (const key of Object.keys(this.registeredPlugins)) {
const plugin = this.registeredPlugins[key]
await this.addCSSToGlobalFile(plugin.path, plugin.css)
}
}
private getRegisteredPluginsOrThemes (type: PluginType) { private getRegisteredPluginsOrThemes (type: PluginType) {
const plugins: RegisteredPlugin[] = [] const plugins: RegisteredPlugin[] = []

View File

@ -5,12 +5,14 @@ import { CONFIG } from '../../initializers/config'
import { outputJSON, pathExists } from 'fs-extra' import { outputJSON, pathExists } from 'fs-extra'
import { join } from 'path' import { join } from 'path'
async function installNpmPlugin (name: string, version: string) { async function installNpmPlugin (name: string, version?: string) {
// Security check // Security check
checkNpmPluginNameOrThrow(name) checkNpmPluginNameOrThrow(name)
checkPluginVersionOrThrow(version) if (version) checkPluginVersionOrThrow(version)
let toInstall = name
if (version) toInstall += `@${version}`
const toInstall = `${name}@${version}`
await execYarn('add ' + toInstall) await execYarn('add ' + toInstall)
} }

View File

@ -1,10 +1,11 @@
import * as express from 'express' 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 { logger } from '../../helpers/logger'
import { areValidationErrors } from './utils' 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 { 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 = [ const servePluginStaticDirectoryValidator = [
param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'), 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 { export {
servePluginStaticDirectoryValidator servePluginStaticDirectoryValidator,
updatePluginSettingsValidator,
uninstallPluginValidator,
enabledPluginValidator,
installPluginValidator,
listPluginsValidator
} }

View 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_SERVERS_BLOCKLIST_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.SERVERS_BLOCKLIST)
const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS) const SORTABLE_USER_NOTIFICATIONS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.USER_NOTIFICATIONS)
const SORTABLE_VIDEO_PLAYLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_PLAYLISTS) 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 usersSortValidator = checkSort(SORTABLE_USERS_COLUMNS)
const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_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 serversBlocklistSortValidator = checkSort(SORTABLE_SERVERS_BLOCKLIST_COLUMNS)
const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS) const userNotificationsSortValidator = checkSort(SORTABLE_USER_NOTIFICATIONS_COLUMNS)
const videoPlaylistsSortValidator = checkSort(SORTABLE_VIDEO_PLAYLISTS_COLUMNS) const videoPlaylistsSortValidator = checkSort(SORTABLE_VIDEO_PLAYLISTS_COLUMNS)
const pluginsSortValidator = checkSort(SORTABLE_PLUGINS_COLUMNS)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -63,5 +65,6 @@ export {
accountsBlocklistSortValidator, accountsBlocklistSortValidator,
serversBlocklistSortValidator, serversBlocklistSortValidator,
userNotificationsSortValidator, userNotificationsSortValidator,
videoPlaylistsSortValidator videoPlaylistsSortValidator,
pluginsSortValidator
} }

View File

@ -1,11 +1,20 @@
import { AllowNull, Column, CreatedAt, DataType, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { throwIfNotValid } from '../utils' import { getSort, throwIfNotValid } from '../utils'
import { import {
isPluginDescriptionValid, isPluginDescriptionValid,
isPluginNameValid, isPluginNameValid,
isPluginTypeValid, isPluginTypeValid,
isPluginVersionValid isPluginVersionValid
} from '../../helpers/custom-validators/plugins' } 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({ @Table({
tableName: 'plugin', tableName: 'plugin',
@ -85,14 +94,75 @@ export class PluginModel extends Model<PluginModel> {
return PluginModel.findOne(query) 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 = { const query = {
where: { where: {
name: pluginName 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
}
} }
} }

View File

@ -21,10 +21,10 @@ import { VideoBlacklistModel } from '../models/video/video-blacklist'
import { VideoCaptionModel } from '../models/video/video-caption' import { VideoCaptionModel } from '../models/video/video-caption'
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
import { RegisteredPlugin } from '../lib/plugins/plugin-manager' import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
import { PluginModel } from '../models/server/plugin'
declare module 'express' { declare module 'express' {
interface Response { interface Response {
locals: { locals: {
video?: VideoModel video?: VideoModel
@ -81,6 +81,8 @@ declare module 'express' {
authenticated?: boolean authenticated?: boolean
registeredPlugin?: RegisteredPlugin registeredPlugin?: RegisteredPlugin
plugin?: PluginModel
} }
} }
} }

View File

@ -0,0 +1,3 @@
export interface InstallPlugin {
npmName: string
}

View File

@ -0,0 +1,3 @@
export interface ManagePlugin {
npmName: string
}

View File

@ -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
}

View File

@ -1,6 +1,7 @@
import { RegisterOptions } from './register-options.type' import { RegisterOptions } from './register-options.model'
export interface PluginLibrary { export interface PluginLibrary {
register: (options: RegisterOptions) => void register: (options: RegisterOptions) => void
unregister: () => Promise<any> unregister: () => Promise<any>
} }

View File

@ -0,0 +1,7 @@
import * as Bluebird from 'bluebird'
export interface PluginSettingsManager {
getSetting: (name: string) => Bluebird<string>
setSetting: (name: string, value: string) => Bluebird<any>
}

View File

@ -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
}

View File

@ -1,5 +0,0 @@
import { RegisterHookOptions } from './register.model'
export type RegisterOptions = {
registerHook: (options: RegisterHookOptions) => void
}

View File

@ -0,0 +1,6 @@
export interface RegisterSettingOptions {
name: string
label: string
type: 'input'
default?: string
}

View File

@ -31,5 +31,7 @@ export enum UserRight {
UPDATE_ANY_VIDEO_PLAYLIST, UPDATE_ANY_VIDEO_PLAYLIST,
SEE_ALL_VIDEOS, SEE_ALL_VIDEOS,
CHANGE_VIDEO_OWNERSHIP CHANGE_VIDEO_OWNERSHIP,
MANAGE_PLUGINS
} }