import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants'
import { join } from 'path'
import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager'
-import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins'
+import { getPluginValidator, pluginStaticDirectoryValidator } from '../middlewares/validators/plugins'
import { serveThemeCSSValidator } from '../middlewares/validators/themes'
import { PluginType } from '../../shared/models/plugins/plugin.type'
import { isTestInstance } from '../helpers/core-utils'
)
pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
- servePluginStaticDirectoryValidator(PluginType.PLUGIN),
+ getPluginValidator(PluginType.PLUGIN),
+ pluginStaticDirectoryValidator,
servePluginStaticDirectory
)
pluginsRouter.get('/plugins/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
- servePluginStaticDirectoryValidator(PluginType.PLUGIN),
+ getPluginValidator(PluginType.PLUGIN),
+ pluginStaticDirectoryValidator,
servePluginClientScripts
)
+pluginsRouter.use('/plugins/:pluginName/router',
+ getPluginValidator(PluginType.PLUGIN, false),
+ servePluginCustomRoutes
+)
+
+pluginsRouter.use('/plugins/:pluginName/:pluginVersion/router',
+ getPluginValidator(PluginType.PLUGIN),
+ servePluginCustomRoutes
+)
+
pluginsRouter.get('/themes/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
- servePluginStaticDirectoryValidator(PluginType.THEME),
+ getPluginValidator(PluginType.THEME),
+ pluginStaticDirectoryValidator,
servePluginStaticDirectory
)
pluginsRouter.get('/themes/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
- servePluginStaticDirectoryValidator(PluginType.THEME),
+ getPluginValidator(PluginType.THEME),
+ pluginStaticDirectoryValidator,
servePluginClientScripts
)
const [ directory, ...file ] = staticEndpoint.split('/')
const staticPath = plugin.staticDirs[directory]
- if (!staticPath) {
- return res.sendStatus(404)
- }
+ if (!staticPath) return res.sendStatus(404)
const filepath = file.join('/')
return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions)
}
+function servePluginCustomRoutes (req: express.Request, res: express.Response, next: express.NextFunction) {
+ const plugin: RegisteredPlugin = res.locals.registeredPlugin
+ const router = PluginManager.Instance.getRouter(plugin.npmName)
+
+ if (!router) return res.sendStatus(404)
+
+ return router(req, res, next)
+}
+
function servePluginClientScripts (req: express.Request, res: express.Response) {
const plugin: RegisteredPlugin = res.locals.registeredPlugin
const staticEndpoint = req.params.staticEndpoint
const file = plugin.clientScripts[staticEndpoint]
- if (!file) {
- return res.sendStatus(404)
- }
+ if (!file) return res.sendStatus(404)
return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions)
}
import { PluginType } from '../../../shared/models/plugins/plugin.type'
import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
import { outputFile, readJSON } from 'fs-extra'
-import { ServerHook, ServerHookName, serverHookObject } from '../../../shared/models/plugins/server-hook.model'
+import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server-hook.model'
import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks'
import { RegisterServerOptions } from '../../typings/plugins/register-server-option.model'
import { PluginLibrary } from '../../typings/plugins'
import { ClientHtml } from '../client-html'
-import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model'
-import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
import { PluginTranslation } from '../../../shared/models/plugins/plugin-translation.model'
-import { buildRegisterHelpers, reinitVideoConstants } from './register-helpers'
+import { RegisterHelpersStore } from './register-helpers-store'
+import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model'
export interface RegisteredPlugin {
npmName: string
private static instance: PluginManager
private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
- private settings: { [name: string]: RegisterServerSettingOptions[] } = {}
private hooks: { [name: string]: HookInformationValue[] } = {}
private translations: PluginLocalesTranslations = {}
+ private registerHelpersStore: { [npmName: string]: RegisterHelpersStore } = {}
+
private constructor () {
}
}
getRegisteredSettings (npmName: string) {
- return this.settings[npmName] || []
+ const store = this.registerHelpersStore[npmName]
+ if (store) return store.getSettings()
+
+ return []
+ }
+
+ getRouter (npmName: string) {
+ const store = this.registerHelpersStore[npmName]
+ if (!store) return null
+
+ return store.getRouter()
}
getTranslations (locale: string) {
}
delete this.registeredPlugins[plugin.npmName]
- delete this.settings[plugin.npmName]
this.deleteTranslations(plugin.npmName)
this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName)
}
- reinitVideoConstants(plugin.npmName)
+ const store = this.registerHelpersStore[plugin.npmName]
+ store.reinitVideoConstants(plugin.npmName)
+
+ delete this.registerHelpersStore[plugin.npmName]
logger.info('Regenerating registered plugin CSS to global file.')
await this.regeneratePluginGlobalCSS()
// ###################### Generate register helpers ######################
private getRegisterHelpers (npmName: string, plugin: PluginModel): RegisterServerOptions {
- const registerHook = (options: RegisterServerHookOptions) => {
- if (serverHookObject[options.target] !== true) {
- logger.warn('Unknown hook %s of plugin %s. Skipping.', options.target, npmName)
- return
- }
-
+ const onHookAdded = (options: RegisterServerHookOptions) => {
if (!this.hooks[options.target]) this.hooks[options.target] = []
this.hooks[options.target].push({
- npmName,
+ npmName: npmName,
pluginName: plugin.name,
handler: options.handler,
priority: options.priority || 0
})
}
- const registerSetting = (options: RegisterServerSettingOptions) => {
- if (!this.settings[npmName]) this.settings[npmName] = []
-
- this.settings[npmName].push(options)
- }
-
- const registerHelpers = buildRegisterHelpers(npmName, plugin)
+ const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this))
+ this.registerHelpersStore[npmName] = registerHelpersStore
- return Object.assign(registerHelpers, {
- registerHook,
- registerSetting
- })
+ return registerHelpersStore.buildRegisterHelpers()
}
private sanitizeAndCheckPackageJSONOrThrow (packageJSON: PluginPackageJson, pluginType: PluginType) {
--- /dev/null
+import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model'
+import { PluginModel } from '@server/models/server/plugin'
+import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model'
+import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model'
+import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '@server/initializers/constants'
+import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model'
+import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model'
+import { RegisterServerOptions } from '@server/typings/plugins'
+import { buildPluginHelpers } from './plugin-helpers'
+import { logger } from '@server/helpers/logger'
+import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model'
+import { serverHookObject } from '@shared/models/plugins/server-hook.model'
+import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model'
+import * as express from 'express'
+
+type AlterableVideoConstant = 'language' | 'licence' | 'category'
+type VideoConstant = { [key in number | string]: string }
+
+type UpdatedVideoConstant = {
+ [name in AlterableVideoConstant]: {
+ added: { key: number | string, label: string }[]
+ deleted: { key: number | string, label: string }[]
+ }
+}
+
+export class RegisterHelpersStore {
+ private readonly updatedVideoConstants: UpdatedVideoConstant = {
+ language: { added: [], deleted: [] },
+ licence: { added: [], deleted: [] },
+ category: { added: [], deleted: [] }
+ }
+
+ private readonly settings: RegisterServerSettingOptions[] = []
+
+ private readonly router: express.Router
+
+ constructor (
+ private readonly npmName: string,
+ private readonly plugin: PluginModel,
+ private readonly onHookAdded: (options: RegisterServerHookOptions) => void
+ ) {
+ this.router = express.Router()
+ }
+
+ buildRegisterHelpers (): RegisterServerOptions {
+ const registerHook = this.buildRegisterHook()
+ const registerSetting = this.buildRegisterSetting()
+
+ const getRouter = this.buildGetRouter()
+
+ const settingsManager = this.buildSettingsManager()
+ const storageManager = this.buildStorageManager()
+
+ const videoLanguageManager = this.buildVideoLanguageManager()
+
+ const videoLicenceManager = this.buildVideoLicenceManager()
+ const videoCategoryManager = this.buildVideoCategoryManager()
+
+ const peertubeHelpers = buildPluginHelpers(this.npmName)
+
+ return {
+ registerHook,
+ registerSetting,
+
+ getRouter,
+
+ settingsManager,
+ storageManager,
+
+ videoLanguageManager,
+ videoCategoryManager,
+ videoLicenceManager,
+
+ peertubeHelpers
+ }
+ }
+
+ reinitVideoConstants (npmName: string) {
+ const hash = {
+ language: VIDEO_LANGUAGES,
+ licence: VIDEO_LICENCES,
+ category: VIDEO_CATEGORIES
+ }
+ const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category' ]
+
+ for (const type of types) {
+ const updatedConstants = this.updatedVideoConstants[type][npmName]
+ if (!updatedConstants) continue
+
+ for (const added of updatedConstants.added) {
+ delete hash[type][added.key]
+ }
+
+ for (const deleted of updatedConstants.deleted) {
+ hash[type][deleted.key] = deleted.label
+ }
+
+ delete this.updatedVideoConstants[type][npmName]
+ }
+ }
+
+ getSettings () {
+ return this.settings
+ }
+
+ getRouter () {
+ return this.router
+ }
+
+ private buildGetRouter () {
+ return () => this.router
+ }
+
+ private buildRegisterSetting () {
+ return (options: RegisterServerSettingOptions) => {
+ this.settings.push(options)
+ }
+ }
+
+ private buildRegisterHook () {
+ return (options: RegisterServerHookOptions) => {
+ if (serverHookObject[options.target] !== true) {
+ logger.warn('Unknown hook %s of plugin %s. Skipping.', options.target, this.npmName)
+ return
+ }
+
+ return this.onHookAdded(options)
+ }
+ }
+
+ private buildSettingsManager (): PluginSettingsManager {
+ return {
+ getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name),
+
+ setSetting: (name: string, value: string) => PluginModel.setSetting(this.plugin.name, this.plugin.type, name, value)
+ }
+ }
+
+ private buildStorageManager (): PluginStorageManager {
+ return {
+ getData: (key: string) => PluginModel.getData(this.plugin.name, this.plugin.type, key),
+
+ storeData: (key: string, data: any) => PluginModel.storeData(this.plugin.name, this.plugin.type, key, data)
+ }
+ }
+
+ private buildVideoLanguageManager (): PluginVideoLanguageManager {
+ return {
+ addLanguage: (key: string, label: string) => {
+ return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label })
+ },
+
+ deleteLanguage: (key: string) => {
+ return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
+ }
+ }
+ }
+
+ private buildVideoCategoryManager (): PluginVideoCategoryManager {
+ return {
+ addCategory: (key: number, label: string) => {
+ return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
+ },
+
+ deleteCategory: (key: number) => {
+ return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
+ }
+ }
+ }
+
+ private buildVideoLicenceManager (): PluginVideoLicenceManager {
+ return {
+ addLicence: (key: number, label: string) => {
+ return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
+ },
+
+ deleteLicence: (key: number) => {
+ return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key })
+ }
+ }
+ }
+
+ private addConstant<T extends string | number> (parameters: {
+ npmName: string
+ type: AlterableVideoConstant
+ obj: VideoConstant
+ key: T
+ label: string
+ }) {
+ const { npmName, type, obj, key, label } = parameters
+
+ if (obj[key]) {
+ logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
+ return false
+ }
+
+ if (!this.updatedVideoConstants[type][npmName]) {
+ this.updatedVideoConstants[type][npmName] = {
+ added: [],
+ deleted: []
+ }
+ }
+
+ this.updatedVideoConstants[type][npmName].added.push({ key, label })
+ obj[key] = label
+
+ return true
+ }
+
+ private deleteConstant<T extends string | number> (parameters: {
+ npmName: string
+ type: AlterableVideoConstant
+ obj: VideoConstant
+ key: T
+ }) {
+ const { npmName, type, obj, key } = parameters
+
+ if (!obj[key]) {
+ logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key)
+ return false
+ }
+
+ if (!this.updatedVideoConstants[type][npmName]) {
+ this.updatedVideoConstants[type][npmName] = {
+ added: [],
+ deleted: []
+ }
+ }
+
+ this.updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] })
+ delete obj[key]
+
+ return true
+ }
+}
+++ /dev/null
-import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model'
-import { PluginModel } from '@server/models/server/plugin'
-import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model'
-import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model'
-import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '@server/initializers/constants'
-import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model'
-import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model'
-import { RegisterServerOptions } from '@server/typings/plugins'
-import { buildPluginHelpers } from './plugin-helpers'
-import { logger } from '@server/helpers/logger'
-
-type AlterableVideoConstant = 'language' | 'licence' | 'category'
-type VideoConstant = { [key in number | string]: string }
-type UpdatedVideoConstant = {
- [name in AlterableVideoConstant]: {
- [npmName: string]: {
- added: { key: number | string, label: string }[]
- deleted: { key: number | string, label: string }[]
- }
- }
-}
-
-const updatedVideoConstants: UpdatedVideoConstant = {
- language: {},
- licence: {},
- category: {}
-}
-
-function buildRegisterHelpers (npmName: string, plugin: PluginModel): Omit<RegisterServerOptions, 'registerHook' | 'registerSetting'> {
- const settingsManager = buildSettingsManager(plugin)
- const storageManager = buildStorageManager(plugin)
-
- const videoLanguageManager = buildVideoLanguageManager(npmName)
-
- const videoCategoryManager = buildVideoCategoryManager(npmName)
- const videoLicenceManager = buildVideoLicenceManager(npmName)
-
- const peertubeHelpers = buildPluginHelpers(npmName)
-
- return {
- settingsManager,
- storageManager,
- videoLanguageManager,
- videoCategoryManager,
- videoLicenceManager,
- peertubeHelpers
- }
-}
-
-function reinitVideoConstants (npmName: string) {
- const hash = {
- language: VIDEO_LANGUAGES,
- licence: VIDEO_LICENCES,
- category: VIDEO_CATEGORIES
- }
- const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category' ]
-
- for (const type of types) {
- const updatedConstants = updatedVideoConstants[type][npmName]
- if (!updatedConstants) continue
-
- for (const added of updatedConstants.added) {
- delete hash[type][added.key]
- }
-
- for (const deleted of updatedConstants.deleted) {
- hash[type][deleted.key] = deleted.label
- }
-
- delete updatedVideoConstants[type][npmName]
- }
-}
-
-export {
- buildRegisterHelpers,
- reinitVideoConstants
-}
-
-// ---------------------------------------------------------------------------
-
-function buildSettingsManager (plugin: PluginModel): PluginSettingsManager {
- return {
- getSetting: (name: string) => PluginModel.getSetting(plugin.name, plugin.type, name),
-
- setSetting: (name: string, value: string) => PluginModel.setSetting(plugin.name, plugin.type, name, value)
- }
-}
-
-function buildStorageManager (plugin: PluginModel): PluginStorageManager {
- return {
- getData: (key: string) => PluginModel.getData(plugin.name, plugin.type, key),
-
- storeData: (key: string, data: any) => PluginModel.storeData(plugin.name, plugin.type, key, data)
- }
-}
-
-function buildVideoLanguageManager (npmName: string): PluginVideoLanguageManager {
- return {
- addLanguage: (key: string, label: string) => addConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label }),
-
- deleteLanguage: (key: string) => deleteConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
- }
-}
-
-function buildVideoCategoryManager (npmName: string): PluginVideoCategoryManager {
- return {
- addCategory: (key: number, label: string) => {
- return addConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
- },
-
- deleteCategory: (key: number) => {
- return deleteConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
- }
- }
-}
-
-function buildVideoLicenceManager (npmName: string): PluginVideoLicenceManager {
- return {
- addLicence: (key: number, label: string) => {
- return addConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
- },
-
- deleteLicence: (key: number) => {
- return deleteConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key })
- }
- }
-}
-
-function addConstant<T extends string | number> (parameters: {
- npmName: string
- type: AlterableVideoConstant
- obj: VideoConstant
- key: T
- label: string
-}) {
- const { npmName, type, obj, key, label } = parameters
-
- if (obj[key]) {
- logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
- return false
- }
-
- if (!updatedVideoConstants[type][npmName]) {
- updatedVideoConstants[type][npmName] = {
- added: [],
- deleted: []
- }
- }
-
- updatedVideoConstants[type][npmName].added.push({ key, label })
- obj[key] = label
-
- return true
-}
-
-function deleteConstant<T extends string | number> (parameters: {
- npmName: string
- type: AlterableVideoConstant
- obj: VideoConstant
- key: T
-}) {
- const { npmName, type, obj, key } = parameters
-
- if (!obj[key]) {
- logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key)
- return false
- }
-
- if (!updatedVideoConstants[type][npmName]) {
- updatedVideoConstants[type][npmName] = {
- added: [],
- deleted: []
- }
- }
-
- updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] })
- delete obj[key]
-
- return true
-}
import * as express from 'express'
-import { body, param, query } from 'express-validator'
+import { body, param, query, ValidationChain } from 'express-validator'
import { logger } from '../../helpers/logger'
import { areValidationErrors } from './utils'
import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins'
import { PluginType } from '../../../shared/models/plugins/plugin.type'
import { CONFIG } from '../../initializers/config'
-const servePluginStaticDirectoryValidator = (pluginType: PluginType) => [
- param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
- param('pluginVersion').custom(isPluginVersionValid).withMessage('Should have a valid plugin version'),
- param('staticEndpoint').custom(isSafePath).withMessage('Should have a valid static endpoint'),
+const getPluginValidator = (pluginType: PluginType, withVersion = true) => {
+ const validators: (ValidationChain | express.Handler)[] = [
+ param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name')
+ ]
- (req: express.Request, res: express.Response, next: express.NextFunction) => {
- logger.debug('Checking servePluginStaticDirectory parameters', { parameters: req.params })
+ if (withVersion) {
+ validators.push(
+ param('pluginVersion').custom(isPluginVersionValid).withMessage('Should have a valid plugin version')
+ )
+ }
- if (areValidationErrors(req, res)) return
+ return validators.concat([
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking getPluginValidator parameters', { parameters: req.params })
+
+ if (areValidationErrors(req, res)) return
+
+ const npmName = PluginModel.buildNpmName(req.params.pluginName, pluginType)
+ const plugin = PluginManager.Instance.getRegisteredPluginOrTheme(npmName)
+
+ if (!plugin) return res.sendStatus(404)
+ if (withVersion && plugin.version !== req.params.pluginVersion) return res.sendStatus(404)
- const npmName = PluginModel.buildNpmName(req.params.pluginName, pluginType)
- const plugin = PluginManager.Instance.getRegisteredPluginOrTheme(npmName)
+ res.locals.registeredPlugin = plugin
- if (!plugin || plugin.version !== req.params.pluginVersion) {
- return res.sendStatus(404)
+ return next()
}
+ ])
+}
+
+const pluginStaticDirectoryValidator = [
+ param('staticEndpoint').custom(isSafePath).withMessage('Should have a valid static endpoint'),
- res.locals.registeredPlugin = plugin
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking pluginStaticDirectoryValidator parameters', { parameters: req.params })
+
+ if (areValidationErrors(req, res)) return
return next()
}
// ---------------------------------------------------------------------------
export {
- servePluginStaticDirectoryValidator,
+ pluginStaticDirectoryValidator,
+ getPluginValidator,
updatePluginSettingsValidator,
uninstallPluginValidator,
listAvailablePluginsValidator,
--- /dev/null
+async function register ({
+ getRouter
+}) {
+ const router = getRouter()
+ router.get('/ping', (req, res) => res.json({ message: 'pong' }))
+
+ router.post('/form/post/mirror', (req, res) => {
+ res.json(req.body)
+ })
+}
+
+async function unregister () {
+ return
+}
+
+module.exports = {
+ register,
+ unregister
+}
+
+// ###########################################################################
--- /dev/null
+{
+ "name": "peertube-plugin-test-five",
+ "version": "0.0.1",
+ "description": "Plugin test 5",
+ "engine": {
+ "peertube": ">=1.3.0"
+ },
+ "keywords": [
+ "peertube",
+ "plugin"
+ ],
+ "homepage": "https://github.com/Chocobozzz/PeerTube",
+ "author": "Chocobozzz",
+ "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
+ "library": "./main.js",
+ "staticDirs": {},
+ "css": [],
+ "clientScripts": [],
+ "translations": {}
+}
import './translations'
import './video-constants'
import './plugin-helpers'
+import './plugin-router'
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import 'mocha'
+import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
+import {
+ getPluginTestPath,
+ installPlugin,
+ makeGetRequest,
+ makePostBodyRequest,
+ setAccessTokensToServers, uninstallPlugin
+} from '../../../shared/extra-utils'
+import { expect } from 'chai'
+
+describe('Test plugin helpers', function () {
+ let server: ServerInfo
+ const basePaths = [
+ '/plugins/test-five/router/',
+ '/plugins/test-five/0.0.1/router/'
+ ]
+
+ before(async function () {
+ this.timeout(30000)
+
+ server = await flushAndRunServer(1)
+ await setAccessTokensToServers([ server ])
+
+ await installPlugin({
+ url: server.url,
+ accessToken: server.accessToken,
+ path: getPluginTestPath('-five')
+ })
+ })
+
+ it('Should answer "pong"', async function () {
+ for (const path of basePaths) {
+ const res = await makeGetRequest({
+ url: server.url,
+ path: path + 'ping',
+ statusCodeExpected: 200
+ })
+
+ expect(res.body.message).to.equal('pong')
+ }
+ })
+
+ it('Should mirror post body', async function () {
+ const body = {
+ hello: 'world',
+ riri: 'fifi',
+ loulou: 'picsou'
+ }
+
+ for (const path of basePaths) {
+ const res = await makePostBodyRequest({
+ url: server.url,
+ path: path + 'form/post/mirror',
+ fields: body,
+ statusCodeExpected: 200
+ })
+
+ expect(res.body).to.deep.equal(body)
+ }
+ })
+
+ it('Should remove the plugin and remove the routes', async function () {
+ await uninstallPlugin({
+ url: server.url,
+ accessToken: server.accessToken,
+ npmName: 'peertube-plugin-test-five'
+ })
+
+ for (const path of basePaths) {
+ await makeGetRequest({
+ url: server.url,
+ path: path + 'ping',
+ statusCodeExpected: 404
+ })
+
+ await makePostBodyRequest({
+ url: server.url,
+ path: path + 'ping',
+ fields: {},
+ statusCodeExpected: 404
+ })
+ }
+ })
+
+ after(async function () {
+ await cleanupTests([ server ])
+ })
+})
import { PluginVideoLanguageManager } from '../../../shared/models/plugins/plugin-video-language-manager.model'
import { PluginVideoLicenceManager } from '../../../shared/models/plugins/plugin-video-licence-manager.model'
import { Logger } from 'winston'
+import { Router } from 'express'
export type PeerTubeHelpers = {
logger: Logger
videoLanguageManager: PluginVideoLanguageManager
videoLicenceManager: PluginVideoLicenceManager
+ // Get plugin router to create custom routes
+ // Base routes of this router are
+ // * /plugins/:pluginName/:pluginVersion/router/...
+ // * /plugins/:pluginName/router/...
+ getRouter(): Router
+
peertubeHelpers: PeerTubeHelpers
}
- [Settings](#settings)
- [Storage](#storage)
- [Update video constants](#update-video-constants)
+ - [Add custom routes](#add-custom-routes)
- [Client helpers (themes & plugins)](#client-helpers-themes--plugins)
- [Plugin static route](#plugin-static-route)
- [Translate](#translate)
storageManager,
videoCategoryManager,
videoLicenceManager,
- videoLanguageManager
+ videoLanguageManager,
+ peertubeHelpers,
+ getRouter
}) {
registerHook({
target: 'action:application.listening',
videoLicenceManager.deleteLicence(7) // Public domain
```
+#### Add custom routes
+
+You can create custom routes using an [express Router](https://expressjs.com/en/4x/api.html#router) for your plugin:
+
+```js
+const router = getRouter()
+router.get('/ping', (req, res) => res.json({ message: 'pong' }))
+```
+
+The `ping` route can be accessed using:
+ * `/plugins/:pluginName/:pluginVersion/router/ping`
+ * Or `/plugins/:pluginName/router/ping`
+
+
### Client helpers (themes & plugins)
### Plugin static route