mirror of https://github.com/Chocobozzz/PeerTube
WIP plugins: static files
parent
f023a19c3e
commit
2c0539420d
|
@ -4,9 +4,9 @@ import { PluginManager } from '../../server/lib/plugins/plugin-manager'
|
|||
import { isAbsolute } from 'path'
|
||||
|
||||
program
|
||||
.option('-n, --pluginName [pluginName]', 'Plugin name to install')
|
||||
.option('-v, --pluginVersion [pluginVersion]', 'Plugin version to install')
|
||||
.option('-p, --pluginPath [pluginPath]', 'Path of the plugin you want to install')
|
||||
.option('-n, --plugin-name [pluginName]', 'Plugin name to install')
|
||||
.option('-v, --plugin-version [pluginVersion]', 'Plugin version to install')
|
||||
.option('-p, --plugin-path [pluginPath]', 'Path of the plugin you want to install')
|
||||
.parse(process.argv)
|
||||
|
||||
if (!program['pluginName'] && !program['pluginPath']) {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { initDatabaseModels } from '../../server/initializers/database'
|
||||
import * as program from 'commander'
|
||||
import { PluginManager } from '../../server/lib/plugins/plugin-manager'
|
||||
import { isAbsolute } from 'path'
|
||||
|
||||
program
|
||||
.option('-n, --package-name [packageName]', 'Package name to install')
|
||||
.parse(process.argv)
|
||||
|
||||
if (!program['packageName']) {
|
||||
console.error('You need to specify the plugin name.')
|
||||
process.exit(-1)
|
||||
}
|
||||
|
||||
run()
|
||||
.then(() => process.exit(0))
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
process.exit(-1)
|
||||
})
|
||||
|
||||
async function run () {
|
||||
await initDatabaseModels(true)
|
||||
|
||||
const toUninstall = program['packageName']
|
||||
await PluginManager.Instance.uninstall(toUninstall)
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
import * as express from 'express'
|
||||
import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants'
|
||||
import { join } from 'path'
|
||||
import { basename, join } from 'path'
|
||||
import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
|
||||
import { servePluginStaticDirectoryValidator } from '../middlewares/validators/plugins'
|
||||
|
||||
const pluginsRouter = express.Router()
|
||||
|
||||
pluginsRouter.get('/global.css',
|
||||
express.static(PLUGIN_GLOBAL_CSS_PATH, { fallthrough: false })
|
||||
servePluginGlobalCSS
|
||||
)
|
||||
|
||||
pluginsRouter.get('/:pluginName/:pluginVersion/statics/:staticEndpoint',
|
||||
pluginsRouter.get('/:pluginName/:pluginVersion/static/:staticEndpoint(*)',
|
||||
servePluginStaticDirectoryValidator,
|
||||
servePluginStaticDirectory
|
||||
)
|
||||
|
||||
pluginsRouter.get('/:pluginName/:pluginVersion/client-scripts/:staticEndpoint',
|
||||
pluginsRouter.get('/:pluginName/:pluginVersion/client-scripts/:staticEndpoint(*)',
|
||||
servePluginStaticDirectoryValidator,
|
||||
servePluginClientScripts
|
||||
)
|
||||
|
@ -28,21 +28,33 @@ export {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function servePluginGlobalCSS (req: express.Request, res: express.Response) {
|
||||
return res.sendFile(PLUGIN_GLOBAL_CSS_PATH)
|
||||
}
|
||||
|
||||
function servePluginStaticDirectory (req: express.Request, res: express.Response) {
|
||||
const plugin: RegisteredPlugin = res.locals.registeredPlugin
|
||||
const staticEndpoint = req.params.staticEndpoint
|
||||
|
||||
const staticPath = plugin.staticDirs[staticEndpoint]
|
||||
const [ directory, ...file ] = staticEndpoint.split('/')
|
||||
|
||||
const staticPath = plugin.staticDirs[directory]
|
||||
if (!staticPath) {
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
|
||||
return express.static(join(plugin.path, staticPath), { fallthrough: false })
|
||||
const filepath = file.join('/')
|
||||
return res.sendFile(join(plugin.path, staticPath, filepath))
|
||||
}
|
||||
|
||||
function servePluginClientScripts (req: express.Request, res: express.Response) {
|
||||
const plugin: RegisteredPlugin = res.locals.registeredPlugin
|
||||
const staticEndpoint = req.params.staticEndpoint
|
||||
|
||||
return express.static(join(plugin.path, staticEndpoint), { fallthrough: false })
|
||||
const file = plugin.clientScripts[staticEndpoint]
|
||||
if (!file) {
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
|
||||
return res.sendFile(join(plugin.path, staticEndpoint))
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ import { RegisterHookOptions } from '../../../shared/models/plugins/register.mod
|
|||
import { basename, join } from 'path'
|
||||
import { CONFIG } from '../../initializers/config'
|
||||
import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins'
|
||||
import { PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
|
||||
import { ClientScript, PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
|
||||
import { PluginLibrary } from '../../../shared/models/plugins/plugin-library.model'
|
||||
import { createReadStream, createWriteStream } from 'fs'
|
||||
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'
|
||||
|
||||
export interface RegisteredPlugin {
|
||||
name: string
|
||||
|
@ -22,6 +23,7 @@ export interface RegisteredPlugin {
|
|||
path: string
|
||||
|
||||
staticDirs: { [name: string]: string }
|
||||
clientScripts: { [name: string]: ClientScript }
|
||||
|
||||
css: string[]
|
||||
|
||||
|
@ -46,6 +48,8 @@ export class PluginManager {
|
|||
}
|
||||
|
||||
async registerPlugins () {
|
||||
await this.resetCSSGlobalFile()
|
||||
|
||||
const plugins = await PluginModel.listEnabledPluginsAndThemes()
|
||||
|
||||
for (const plugin of plugins) {
|
||||
|
@ -83,6 +87,16 @@ export class PluginManager {
|
|||
}
|
||||
|
||||
await plugin.unregister()
|
||||
|
||||
// Remove hooks of this plugin
|
||||
for (const key of Object.keys(this.hooks)) {
|
||||
this.hooks[key] = this.hooks[key].filter(h => h.pluginName !== name)
|
||||
}
|
||||
|
||||
delete this.registeredPlugins[plugin.name]
|
||||
|
||||
logger.info('Regenerating registered plugin CSS to global file.')
|
||||
await this.regeneratePluginGlobalCSS()
|
||||
}
|
||||
|
||||
async install (toInstall: string, version: string, fromDisk = false) {
|
||||
|
@ -132,9 +146,30 @@ export class PluginManager {
|
|||
}
|
||||
|
||||
async uninstall (packageName: string) {
|
||||
await PluginModel.uninstall(this.normalizePluginName(packageName))
|
||||
logger.info('Uninstalling plugin %s.', packageName)
|
||||
|
||||
const pluginName = this.normalizePluginName(packageName)
|
||||
|
||||
try {
|
||||
await this.unregister(pluginName)
|
||||
} catch (err) {
|
||||
logger.warn('Cannot unregister plugin %s.', pluginName, { err })
|
||||
}
|
||||
|
||||
const plugin = await PluginModel.load(pluginName)
|
||||
if (!plugin || plugin.uninstalled === true) {
|
||||
logger.error('Cannot uninstall plugin %s: it does not exist or is already uninstalled.', packageName)
|
||||
return
|
||||
}
|
||||
|
||||
plugin.enabled = false
|
||||
plugin.uninstalled = true
|
||||
|
||||
await plugin.save()
|
||||
|
||||
await removeNpmPlugin(packageName)
|
||||
|
||||
logger.info('Plugin %s uninstalled.', packageName)
|
||||
}
|
||||
|
||||
private async registerPluginOrTheme (plugin: PluginModel) {
|
||||
|
@ -152,6 +187,11 @@ export class PluginManager {
|
|||
library = await this.registerPlugin(plugin, pluginPath, packageJSON)
|
||||
}
|
||||
|
||||
const clientScripts: { [id: string]: ClientScript } = {}
|
||||
for (const c of packageJSON.clientScripts) {
|
||||
clientScripts[c.script] = c
|
||||
}
|
||||
|
||||
this.registeredPlugins[ plugin.name ] = {
|
||||
name: plugin.name,
|
||||
type: plugin.type,
|
||||
|
@ -160,6 +200,7 @@ export class PluginManager {
|
|||
peertubeEngine: plugin.peertubeEngine,
|
||||
path: pluginPath,
|
||||
staticDirs: packageJSON.staticDirs,
|
||||
clientScripts,
|
||||
css: packageJSON.css,
|
||||
unregister: library ? library.unregister : undefined
|
||||
}
|
||||
|
@ -199,6 +240,10 @@ export class PluginManager {
|
|||
}
|
||||
}
|
||||
|
||||
private resetCSSGlobalFile () {
|
||||
return outputFile(PLUGIN_GLOBAL_CSS_PATH, '')
|
||||
}
|
||||
|
||||
private async addCSSToGlobalFile (pluginPath: string, cssRelativePaths: string[]) {
|
||||
for (const cssPath of cssRelativePaths) {
|
||||
await this.concatFiles(join(pluginPath, cssPath), PLUGIN_GLOBAL_CSS_PATH)
|
||||
|
@ -207,8 +252,8 @@ export class PluginManager {
|
|||
|
||||
private concatFiles (input: string, output: string) {
|
||||
return new Promise<void>((res, rej) => {
|
||||
const outputStream = createWriteStream(input)
|
||||
const inputStream = createReadStream(output)
|
||||
const inputStream = createReadStream(input)
|
||||
const outputStream = createWriteStream(output, { flags: 'a' })
|
||||
|
||||
inputStream.pipe(outputStream)
|
||||
|
||||
|
@ -233,6 +278,16 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
static get Instance () {
|
||||
return this.instance || (this.instance = new this())
|
||||
}
|
||||
|
|
|
@ -75,6 +75,16 @@ export class PluginModel extends Model<PluginModel> {
|
|||
return PluginModel.findAll(query)
|
||||
}
|
||||
|
||||
static load (pluginName: string) {
|
||||
const query = {
|
||||
where: {
|
||||
name: pluginName
|
||||
}
|
||||
}
|
||||
|
||||
return PluginModel.findOne(query)
|
||||
}
|
||||
|
||||
static uninstall (pluginName: string) {
|
||||
const query = {
|
||||
where: {
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
export type ClientScript = {
|
||||
script: string,
|
||||
scopes: string[]
|
||||
}
|
||||
|
||||
export type PluginPackageJson = {
|
||||
name: string
|
||||
version: string
|
||||
|
@ -12,5 +17,5 @@ export type PluginPackageJson = {
|
|||
staticDirs: { [ name: string ]: string }
|
||||
css: string[]
|
||||
|
||||
clientScripts: { script: string, scopes: string[] }[]
|
||||
clientScripts: ClientScript[]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue