"clean:server:test": "scripty",
"watch:client": "scripty",
"watch:server": "scripty",
- "plugin:install": "node ./dist/scripts/plugin/install.js",
- "plugin:uninstall": "node ./dist/scripts/plugin/uninstall.js",
"danger:clean:dev": "scripty",
"danger:clean:prod": "scripty",
"danger:clean:modules": "scripty",
"dev": "scripty",
"dev:server": "scripty",
"dev:client": "scripty",
+ "dev:cli": "scripty",
"start": "node dist/server",
"start:server": "node dist/server --no-client",
"update-host": "node ./dist/scripts/update-host.js",
--- /dev/null
+#!/bin/sh
+
+set -eu
+
+rm -rf ./dist/server/tools/
+
+(
+ cd ./server/tools
+ yarn install --pure-lockfile
+)
+
+mkdir -p "./dist/server/tools"
+cp -r "./server/tools/node_modules" "./dist/server/tools"
+
+npm run tsc -- --watch --project ./server/tools/tsconfig.json
+++ /dev/null
-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, --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']) {
- console.error('You need to specify a plugin name with the desired version, or a plugin path.')
- process.exit(-1)
-}
-
-if (program['pluginName'] && !program['pluginVersion']) {
- console.error('You need to specify a the version of the plugin you want to install.')
- process.exit(-1)
-}
-
-if (program['pluginPath'] && !isAbsolute(program['pluginPath'])) {
- console.error('Plugin path should be absolute.')
- process.exit(-1)
-}
-
-run()
- .then(() => process.exit(0))
- .catch(err => {
- console.error(err)
- process.exit(-1)
- })
-
-async function run () {
- await initDatabaseModels(true)
-
- const toInstall = program['pluginName'] || program['pluginPath']
- await PluginManager.Instance.install(toInstall, program['pluginVersion'], !!program['pluginPath'])
-}
+++ /dev/null
-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)
-}
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'
+import { logger } from '../../helpers/logger'
const pluginRouter = express.Router()
authenticate,
ensureUserHasRight(UserRight.MANAGE_PLUGINS),
asyncMiddleware(existingPluginValidator),
- asyncMiddleware(getPluginRegisteredSettings)
+ getPluginRegisteredSettings
)
pluginRouter.put('/:npmName/settings',
async function installPlugin (req: express.Request, res: express.Response) {
const body: InstallPlugin = req.body
- await PluginManager.Instance.install(body.npmName)
+ const fromDisk = !!body.path
+ const toInstall = body.npmName || body.path
+ try {
+ await PluginManager.Instance.install(toInstall, undefined, fromDisk)
+ } catch (err) {
+ logger.warn('Cannot install plugin %s.', toInstall, { err })
+ return res.sendStatus(400)
+ }
return res.sendStatus(204)
}
return res.sendStatus(204)
}
-async function getPluginRegisteredSettings (req: express.Request, res: express.Response) {
+function getPluginRegisteredSettings (req: express.Request, res: express.Response) {
const plugin = res.locals.plugin
- const settings = await PluginManager.Instance.getSettings(plugin.name)
+ const settings = PluginManager.Instance.getSettings(plugin.name)
return res.json({
settings
+++ /dev/null
-import * as express from 'express'
-import { join } from 'path'
-import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
-import { serveThemeCSSValidator } from '../middlewares/validators/themes'
-
-const themesRouter = express.Router()
-
-themesRouter.get('/:themeName/:themeVersion/css/:staticEndpoint(*)',
- serveThemeCSSValidator,
- serveThemeCSSDirectory
-)
-
-// ---------------------------------------------------------------------------
-
-export {
- themesRouter
-}
-
-// ---------------------------------------------------------------------------
-
-function serveThemeCSSDirectory (req: express.Request, res: express.Response) {
- const plugin: RegisteredPlugin = res.locals.registeredPlugin
- const staticEndpoint = req.params.staticEndpoint
-
- if (plugin.css.includes(staticEndpoint) === false) {
- return res.sendStatus(404)
- }
-
- return res.sendFile(join(plugin.path, staticEndpoint))
-}
Useful to avoid circular dependencies.
*/
-import * as bcrypt from 'bcrypt'
import * as createTorrent from 'create-torrent'
import { createHash, HexBase64Latin1Encoding, pseudoRandomBytes } from 'crypto'
import { isAbsolute, join } from 'path'
const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
-const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
-const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
-const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
const createTorrentPromise = promisify2<string, any, any>(createTorrent)
const execPromise2 = promisify2<string, any, string>(exec)
const execPromise = promisify1<string, string>(exec)
promisify0,
promisify1,
+ promisify2,
pseudoRandomBytesPromise,
createPrivateKey,
getPublicKey,
- bcryptComparePromise,
- bcryptGenSaltPromise,
- bcryptHashPromise,
createTorrentPromise,
execPromise2,
execPromise
function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
if (!videoChannel) {
- ``
+ res.status(404)
+ .json({ error: 'Video channel not found' })
+ .end()
return false
}
import { Request } from 'express'
import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
import { ActorModel } from '../models/activitypub/actor'
-import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils'
+import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils'
import { jsig, jsonld } from './custom-jsonld-signature'
import { logger } from './logger'
import { cloneDeep } from 'lodash'
import { createVerify } from 'crypto'
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
+import * as bcrypt from 'bcrypt'
+
+const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
+const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
+const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
const httpSignature = require('http-signature')
cryptPassword,
signJsonLDObject
}
+
+// ---------------------------------------------------------------------------
import { PluginManager } from '../../lib/plugins/plugin-manager'
import { isBooleanValid, isSafePath } from '../../helpers/custom-validators/misc'
import { PluginModel } from '../../models/server/plugin'
+import { InstallPlugin } from '../../../shared/models/plugins/install-plugin.model'
const servePluginStaticDirectoryValidator = [
param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
]
const installPluginValidator = [
- body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
+ body('npmName')
+ .optional()
+ .custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
+ body('path')
+ .optional()
+ .custom(isSafePath).withMessage('Should have a valid safe path'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking installPluginValidator parameters', { parameters: req.body })
if (areValidationErrors(req, res)) return
+ const body: InstallPlugin = req.body
+ if (!body.path && !body.npmName) {
+ return res.status(400)
+ .json({ error: 'Should have either a npmName or a path' })
+ .end()
+ }
+
return next()
}
]
count: number,
sort: string
}) {
+ const { uninstalled = false } = options
const query: FindAndCountOptions = {
offset: options.start,
limit: options.count,
order: getSort(options.sort),
- where: {}
+ where: {
+ uninstalled
+ }
}
if (options.type) query.where['type'] = options.type
- if (options.uninstalled) query.where['uninstalled'] = options.uninstalled
return PluginModel
.findAndCountAll(query)
})
it('Should not find a remote video channel', async function () {
+ this.timeout(15000)
+
{
const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server3'
const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken)
import { Netrc } from 'netrc-parser'
import { getAppNumber, isTestInstance } from '../helpers/core-utils'
import { join } from 'path'
-import { getVideoChannel, root } from '../../shared/extra-utils'
+import { root } from '../../shared/extra-utils/miscs/miscs'
+import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels'
import { Command } from 'commander'
import { VideoChannel, VideoPrivacy } from '../../shared/models/videos'
})
}
-function getRemoteObjectOrDie (program: any, settings: Settings, netrc: Netrc) {
+function getRemoteObjectOrDie (
+ program: any,
+ settings: Settings,
+ netrc: Netrc
+): { url: string, username: string, password: string } {
if (!program['url'] || !program['username'] || !program['password']) {
// No remote and we don't have program parameters: quit
if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) {
return videoAttributes
}
+function getServerCredentials (program: any) {
+ return Promise.all([ getSettings(), getNetrc() ])
+ .then(([ settings, netrc ]) => {
+ return getRemoteObjectOrDie(program, settings, netrc)
+ })
+}
+
// ---------------------------------------------------------------------------
export {
writeSettings,
deleteSettings,
+ getServerCredentials,
+
buildCommonVideoOptions,
buildVideoAttributesFromCommander
}
import * as program from 'commander'
import * as prompt from 'prompt'
-import { getSettings, writeSettings, getNetrc } from './cli'
-import { isHostValid } from '../helpers/custom-validators/servers'
+import { getNetrc, getSettings, writeSettings } from './cli'
import { isUserUsernameValid } from '../helpers/custom-validators/users'
+import { getAccessToken, login } from '../../shared/extra-utils'
const Table = require('cli-table')
}
}
}, async (_, result) => {
+ // Check credentials
+ try {
+ await getAccessToken(result.url, result.username, result.password)
+ } catch (err) {
+ console.error(err.message)
+ process.exit(-1)
+ }
+
await setInstance(result.url, result.username, result.password, program['default'])
process.exit(0)
import { remove } from 'fs-extra'
import { sha256 } from '../helpers/core-utils'
import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl'
-import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getNetrc, getRemoteObjectOrDie, getSettings } from './cli'
+import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
type UserInfo = {
username: string
.option('-v, --verbose', 'Verbose mode')
.parse(process.argv)
-Promise.all([ getSettings(), getNetrc() ])
- .then(([ settings, netrc ]) => {
- const { url, username, password } = getRemoteObjectOrDie(program, settings, netrc)
+getServerCredentials(command)
+ .then(({ url, username, password }) => {
+ if (!program[ 'targetUrl' ]) {
+ console.error('--targetUrl field is required.')
- if (!program[ 'targetUrl' ]) {
- console.error('--targetUrl field is required.')
-
- process.exit(-1)
- }
+ process.exit(-1)
+ }
- removeEndSlashes(url)
- removeEndSlashes(program[ 'targetUrl' ])
+ removeEndSlashes(url)
+ removeEndSlashes(program[ 'targetUrl' ])
- const user = { username, password }
+ const user = { username, password }
- run(url, user)
- .catch(err => {
- console.error(err)
- process.exit(-1)
- })
- })
+ run(url, user)
+ .catch(err => {
+ console.error(err)
+ process.exit(-1)
+ })
+ })
async function run (url: string, user: UserInfo) {
if (!user.password) {
--- /dev/null
+import * as program from 'commander'
+import { PluginType } from '../../shared/models/plugins/plugin.type'
+import { getAccessToken } from '../../shared/extra-utils/users/login'
+import { getMyUserInformation } from '../../shared/extra-utils/users/users'
+import { installPlugin, listPlugins, uninstallPlugin } from '../../shared/extra-utils/server/plugins'
+import { getServerCredentials } from './cli'
+import { User, UserRole } from '../../shared/models/users'
+import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model'
+import { isAbsolute } from 'path'
+
+const Table = require('cli-table')
+
+program
+ .name('plugins')
+ .usage('[command] [options]')
+
+program
+ .command('list')
+ .description('List installed plugins')
+ .option('-u, --url <url>', 'Server url')
+ .option('-U, --username <username>', 'Username')
+ .option('-p, --password <token>', 'Password')
+ .option('-t, --only-themes', 'List themes only')
+ .option('-P, --only-plugins', 'List plugins only')
+ .action(() => pluginsListCLI())
+
+program
+ .command('install')
+ .description('Install a plugin or a theme')
+ .option('-u, --url <url>', 'Server url')
+ .option('-U, --username <username>', 'Username')
+ .option('-p, --password <token>', 'Password')
+ .option('-P --path <path>', 'Install from a path')
+ .option('-n, --npm-name <npmName>', 'Install from npm')
+ .action((options) => installPluginCLI(options))
+
+program
+ .command('uninstall')
+ .description('Uninstall a plugin or a theme')
+ .option('-u, --url <url>', 'Server url')
+ .option('-U, --username <username>', 'Username')
+ .option('-p, --password <token>', 'Password')
+ .option('-n, --npm-name <npmName>', 'NPM plugin/theme name')
+ .action(options => uninstallPluginCLI(options))
+
+if (!process.argv.slice(2).length) {
+ program.outputHelp()
+}
+
+program.parse(process.argv)
+
+// ----------------------------------------------------------------------------
+
+async function pluginsListCLI () {
+ const { url, username, password } = await getServerCredentials(program)
+ const accessToken = await getAdminTokenOrDie(url, username, password)
+
+ let type: PluginType
+ if (program['onlyThemes']) type = PluginType.THEME
+ if (program['onlyPlugins']) type = PluginType.PLUGIN
+
+ const res = await listPlugins({
+ url,
+ accessToken,
+ start: 0,
+ count: 100,
+ sort: 'name',
+ type
+ })
+ const plugins: PeerTubePlugin[] = res.body.data
+
+ const table = new Table({
+ head: ['name', 'version', 'homepage'],
+ colWidths: [ 50, 10, 50 ]
+ })
+
+ for (const plugin of plugins) {
+ const npmName = plugin.type === PluginType.PLUGIN
+ ? 'peertube-plugin-' + plugin.name
+ : 'peertube-theme-' + plugin.name
+
+ table.push([
+ npmName,
+ plugin.version,
+ plugin.homepage
+ ])
+ }
+
+ console.log(table.toString())
+ process.exit(0)
+}
+
+async function installPluginCLI (options: any) {
+ if (!options['path'] && !options['npmName']) {
+ console.error('You need to specify the npm name or the path of the plugin you want to install.\n')
+ program.outputHelp()
+ process.exit(-1)
+ }
+
+ if (options['path'] && !isAbsolute(options['path'])) {
+ console.error('Path should be absolute.')
+ process.exit(-1)
+ }
+
+ const { url, username, password } = await getServerCredentials(options)
+ const accessToken = await getAdminTokenOrDie(url, username, password)
+
+ try {
+ await installPlugin({
+ url,
+ accessToken,
+ npmName: options['npmName'],
+ path: options['path']
+ })
+ } catch (err) {
+ console.error('Cannot install plugin.', err)
+ process.exit(-1)
+ return
+ }
+
+ console.log('Plugin installed.')
+ process.exit(0)
+}
+
+async function uninstallPluginCLI (options: any) {
+ if (!options['npmName']) {
+ console.error('You need to specify the npm name of the plugin/theme you want to uninstall.\n')
+ program.outputHelp()
+ process.exit(-1)
+ }
+
+ const { url, username, password } = await getServerCredentials(options)
+ const accessToken = await getAdminTokenOrDie(url, username, password)
+
+ try {
+ await uninstallPlugin({
+ url,
+ accessToken,
+ npmName: options[ 'npmName' ]
+ })
+ } catch (err) {
+ console.error('Cannot uninstall plugin.', err)
+ process.exit(-1)
+ return
+ }
+
+ console.log('Plugin uninstalled.')
+ process.exit(0)
+}
+
+async function getAdminTokenOrDie (url: string, username: string, password: string) {
+ const accessToken = await getAccessToken(url, username, password)
+ const resMe = await getMyUserInformation(url, accessToken)
+ const me: User = resMe.body
+
+ if (me.role !== UserRole.ADMINISTRATOR) {
+ console.error('Cannot list plugins if you are not administrator.')
+ process.exit(-1)
+ }
+
+ return accessToken
+}
import * as program from 'commander'
import { access, constants } from 'fs-extra'
import { isAbsolute } from 'path'
-import { getClient, login } from '../../shared/extra-utils'
+import { getAccessToken } from '../../shared/extra-utils'
import { uploadVideo } from '../../shared/extra-utils/'
-import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getNetrc, getRemoteObjectOrDie, getSettings } from './cli'
+import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
let command = program
.name('upload')
command = buildCommonVideoOptions(command)
command
-
.option('-u, --url <url>', 'Server url')
.option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password')
.option('-f, --file <file>', 'Video absolute file path')
.parse(process.argv)
-Promise.all([ getSettings(), getNetrc() ])
- .then(([ settings, netrc ]) => {
- const { url, username, password } = getRemoteObjectOrDie(program, settings, netrc)
-
- if (!program[ 'videoName' ] || !program[ 'file' ]) {
- if (!program[ 'videoName' ]) console.error('--video-name is required.')
- if (!program[ 'file' ]) console.error('--file is required.')
+getServerCredentials(command)
+ .then(({ url, username, password }) => {
+ if (!program[ 'videoName' ] || !program[ 'file' ]) {
+ if (!program[ 'videoName' ]) console.error('--video-name is required.')
+ if (!program[ 'file' ]) console.error('--file is required.')
- process.exit(-1)
- }
+ process.exit(-1)
+ }
- if (isAbsolute(program[ 'file' ]) === false) {
- console.error('File path should be absolute.')
- process.exit(-1)
- }
+ if (isAbsolute(program[ 'file' ]) === false) {
+ console.error('File path should be absolute.')
+ process.exit(-1)
+ }
- run(url, username, password).catch(err => {
- console.error(err)
- process.exit(-1)
- })
- })
+ run(url, username, password).catch(err => {
+ console.error(err)
+ process.exit(-1)
+ })
+ })
async function run (url: string, username: string, password: string) {
- const resClient = await getClient(url)
- const client = {
- id: resClient.body.client_id,
- secret: resClient.body.client_secret
- }
-
- const user = { username, password }
-
- let accessToken: string
- try {
- const res = await login(url, client, user)
- accessToken = res.body.access_token
- } catch (err) {
- throw new Error('Cannot authenticate. Please check your username/password.')
- }
+ const accessToken = await getAccessToken(url, username, password)
await access(program[ 'file' ], constants.F_OK)
.command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token')
.command('watch', 'watch a video in the terminal ✩°。⋆').alias('w')
.command('repl', 'initiate a REPL to access internals')
+ .command('plugins [action]', 'manage plugins on a local instance').alias('p')
/* Not Yet Implemented */
program
- .command('plugins [action]',
- 'manage plugins on a local instance',
- { noHelp: true } as program.CommandOptions
- ).alias('p')
.command('diagnostic [action]',
'like couple therapy, but for your instance',
{ noHelp: true } as program.CommandOptions
export * from './requests/requests'
export * from './requests/check-api-params'
export * from './server/servers'
+export * from './server/plugins'
export * from './videos/services'
export * from './videos/video-playlists'
export * from './users/users'
import * as ffmpeg from 'fluent-ffmpeg'
const expect = chai.expect
-let webtorrent = new WebTorrent()
+let webtorrent: WebTorrent.Instance
function immutableAssign <T, U> (target: T, source: U) {
return Object.assign<{}, T, U>({}, target, source)
}
function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
+ const WebTorrent = require('webtorrent')
+
+ if (!webtorrent) webtorrent = new WebTorrent()
if (refreshWebTorrent === true) webtorrent = new WebTorrent()
return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
--- /dev/null
+import { makeGetRequest, makePostBodyRequest } from '../requests/requests'
+import { PluginType } from '../../models/plugins/plugin.type'
+
+function listPlugins (parameters: {
+ url: string,
+ accessToken: string,
+ start?: number,
+ count?: number,
+ sort?: string,
+ type?: PluginType,
+ expectedStatus?: number
+}) {
+ const { url, accessToken, start, count, sort, type, expectedStatus = 200 } = parameters
+ const path = '/api/v1/plugins'
+
+ return makeGetRequest({
+ url,
+ path,
+ token: accessToken,
+ query: {
+ start,
+ count,
+ sort,
+ type
+ },
+ statusCodeExpected: expectedStatus
+ })
+}
+
+function getPlugin (parameters: {
+ url: string,
+ accessToken: string,
+ npmName: string,
+ expectedStatus?: number
+}) {
+ const { url, accessToken, npmName, expectedStatus = 200 } = parameters
+ const path = '/api/v1/plugins/' + npmName
+
+ return makeGetRequest({
+ url,
+ path,
+ token: accessToken,
+ statusCodeExpected: expectedStatus
+ })
+}
+
+function getPluginSettings (parameters: {
+ url: string,
+ accessToken: string,
+ npmName: string,
+ expectedStatus?: number
+}) {
+ const { url, accessToken, npmName, expectedStatus = 200 } = parameters
+ const path = '/api/v1/plugins/' + npmName + '/settings'
+
+ return makeGetRequest({
+ url,
+ path,
+ token: accessToken,
+ statusCodeExpected: expectedStatus
+ })
+}
+
+function getPluginRegisteredSettings (parameters: {
+ url: string,
+ accessToken: string,
+ npmName: string,
+ expectedStatus?: number
+}) {
+ const { url, accessToken, npmName, expectedStatus = 200 } = parameters
+ const path = '/api/v1/plugins/' + npmName + '/registered-settings'
+
+ return makeGetRequest({
+ url,
+ path,
+ token: accessToken,
+ statusCodeExpected: expectedStatus
+ })
+}
+
+function installPlugin (parameters: {
+ url: string,
+ accessToken: string,
+ path?: string,
+ npmName?: string
+ expectedStatus?: number
+}) {
+ const { url, accessToken, npmName, path, expectedStatus = 204 } = parameters
+ const apiPath = '/api/v1/plugins/install'
+
+ return makePostBodyRequest({
+ url,
+ path: apiPath,
+ token: accessToken,
+ fields: { npmName, path },
+ statusCodeExpected: expectedStatus
+ })
+}
+
+function uninstallPlugin (parameters: {
+ url: string,
+ accessToken: string,
+ npmName: string
+ expectedStatus?: number
+}) {
+ const { url, accessToken, npmName, expectedStatus = 204 } = parameters
+ const apiPath = '/api/v1/plugins/uninstall'
+
+ return makePostBodyRequest({
+ url,
+ path: apiPath,
+ token: accessToken,
+ fields: { npmName },
+ statusCodeExpected: expectedStatus
+ })
+}
+
+export {
+ listPlugins,
+ installPlugin,
+ getPlugin,
+ uninstallPlugin,
+ getPluginSettings,
+ getPluginRegisteredSettings
+}
import { ChildProcess, exec, fork } from 'child_process'
import { join } from 'path'
import { root, wait } from '../miscs/miscs'
-import { copy, readdir, readFile, remove } from 'fs-extra'
+import { copy, pathExists, readdir, readFile, remove } from 'fs-extra'
import { existsSync } from 'fs'
import { expect } from 'chai'
import { VideoChannel } from '../../models/videos'
return server
}
-async function checkTmpIsEmpty (server: ServerInfo) {
- return checkDirectoryIsEmpty(server, 'tmp')
+function checkTmpIsEmpty (server: ServerInfo) {
+ return checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css' ])
}
-async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) {
+async function checkDirectoryIsEmpty (server: ServerInfo, directory: string, exceptions: string[] = []) {
const testDirectory = 'test' + server.internalServerNumber
const directoryPath = join(root(), testDirectory, directory)
- const directoryExists = existsSync(directoryPath)
+ const directoryExists = await pathExists(directoryPath)
expect(directoryExists).to.be.true
const files = await readdir(directoryPath)
- expect(files).to.have.lengthOf(0)
+ const filtered = files.filter(f => exceptions.includes(f) === false)
+
+ expect(filtered).to.have.lengthOf(0)
}
function killallServers (servers: ServerInfo[]) {
import * as request from 'supertest'
import { ServerInfo } from '../server/servers'
+import { getClient } from '../server/clients'
type Client = { id: string, secret: string }
type User = { username: string, password: string }
return res.body.access_token as string
}
+async function getAccessToken (url: string, username: string, password: string) {
+ const resClient = await getClient(url)
+ const client = {
+ id: resClient.body.client_id,
+ secret: resClient.body.client_secret
+ }
+
+ const user = { username, password }
+
+ try {
+ const res = await login(url, client, user)
+ return res.body.access_token
+ } catch (err) {
+ throw new Error('Cannot authenticate. Please check your username/password.')
+ }
+}
+
function setAccessTokensToServers (servers: ServerInfo[]) {
const tasks: Promise<any>[] = []
login,
serverLogin,
userLogin,
+ getAccessToken,
setAccessTokensToServers,
Server,
Client,
import * as request from 'supertest'
-import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests'
-
-import { UserCreate, UserRole } from '../../index'
+import { makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests'
import { NSFWPolicyType } from '../../models/videos/nsfw-policy.type'
-import { ServerInfo, userLogin } from '..'
import { UserAdminFlag } from '../../models/users/user-flag.model'
import { UserRegister } from '../../models/users/user-register.model'
+import { UserRole } from '../../models/users/user-role'
+import { ServerInfo } from '../server/servers'
+import { userLogin } from './login'
type CreateUserArgs = { url: string,
accessToken: string,
import * as request from 'supertest'
-import { VideoChannelCreate, VideoChannelUpdate } from '../../models/videos'
+import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
+import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
import { makeGetRequest, updateAvatarRequest } from '../requests/requests'
-import { getMyUserInformation, ServerInfo } from '..'
-import { User } from '../..'
+import { ServerInfo } from '../server/servers'
+import { User } from '../../models/users/user.model'
+import { getMyUserInformation } from '../users/users'
function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
const path = '/api/v1/video-channels'
export interface InstallPlugin {
- npmName: string
+ npmName?: string
+ path?: string
}