aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-07-11 17:23:24 +0200
committerChocobozzz <chocobozzz@cpy.re>2019-07-24 10:58:16 +0200
commit8d2be0ed7bb87283a1ec98609df6b82d83db706a (patch)
tree31a36b252df32be83ceb77658a53b57f9d15e8ac /server
parentdba85a1e9e9f603ba52e1ea42deaf3fdd799b1d8 (diff)
downloadPeerTube-8d2be0ed7bb87283a1ec98609df6b82d83db706a.tar.gz
PeerTube-8d2be0ed7bb87283a1ec98609df6b82d83db706a.tar.zst
PeerTube-8d2be0ed7bb87283a1ec98609df6b82d83db706a.zip
WIP plugins: move plugin CLI in peertube script
Install/uninstall/list plugins remotely
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/plugins.ts16
-rw-r--r--server/controllers/themes.ts30
-rw-r--r--server/helpers/core-utils.ts8
-rw-r--r--server/helpers/custom-validators/video-channels.ts4
-rw-r--r--server/helpers/peertube-crypto.ts9
-rw-r--r--server/middlewares/validators/plugins.ts15
-rw-r--r--server/models/server/plugin.ts6
-rw-r--r--server/tests/api/search/search-activitypub-video-channels.ts2
-rw-r--r--server/tools/cli.ts18
-rw-r--r--server/tools/peertube-auth.ts12
-rw-r--r--server/tools/peertube-import-videos.ts34
-rw-r--r--server/tools/peertube-plugins.ts162
-rw-r--r--server/tools/peertube-upload.ts55
-rw-r--r--server/tools/peertube.ts5
14 files changed, 268 insertions, 108 deletions
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts
index f17e8cab9..8e59f27cf 100644
--- a/server/controllers/api/plugins.ts
+++ b/server/controllers/api/plugins.ts
@@ -21,6 +21,7 @@ import {
21import { PluginManager } from '../../lib/plugins/plugin-manager' 21import { PluginManager } from '../../lib/plugins/plugin-manager'
22import { InstallPlugin } from '../../../shared/models/plugins/install-plugin.model' 22import { InstallPlugin } from '../../../shared/models/plugins/install-plugin.model'
23import { ManagePlugin } from '../../../shared/models/plugins/manage-plugin.model' 23import { ManagePlugin } from '../../../shared/models/plugins/manage-plugin.model'
24import { logger } from '../../helpers/logger'
24 25
25const pluginRouter = express.Router() 26const pluginRouter = express.Router()
26 27
@@ -46,7 +47,7 @@ pluginRouter.get('/:npmName/registered-settings',
46 authenticate, 47 authenticate,
47 ensureUserHasRight(UserRight.MANAGE_PLUGINS), 48 ensureUserHasRight(UserRight.MANAGE_PLUGINS),
48 asyncMiddleware(existingPluginValidator), 49 asyncMiddleware(existingPluginValidator),
49 asyncMiddleware(getPluginRegisteredSettings) 50 getPluginRegisteredSettings
50) 51)
51 52
52pluginRouter.put('/:npmName/settings', 53pluginRouter.put('/:npmName/settings',
@@ -101,7 +102,14 @@ function getPlugin (req: express.Request, res: express.Response) {
101async function installPlugin (req: express.Request, res: express.Response) { 102async function installPlugin (req: express.Request, res: express.Response) {
102 const body: InstallPlugin = req.body 103 const body: InstallPlugin = req.body
103 104
104 await PluginManager.Instance.install(body.npmName) 105 const fromDisk = !!body.path
106 const toInstall = body.npmName || body.path
107 try {
108 await PluginManager.Instance.install(toInstall, undefined, fromDisk)
109 } catch (err) {
110 logger.warn('Cannot install plugin %s.', toInstall, { err })
111 return res.sendStatus(400)
112 }
105 113
106 return res.sendStatus(204) 114 return res.sendStatus(204)
107} 115}
@@ -114,10 +122,10 @@ async function uninstallPlugin (req: express.Request, res: express.Response) {
114 return res.sendStatus(204) 122 return res.sendStatus(204)
115} 123}
116 124
117async function getPluginRegisteredSettings (req: express.Request, res: express.Response) { 125function getPluginRegisteredSettings (req: express.Request, res: express.Response) {
118 const plugin = res.locals.plugin 126 const plugin = res.locals.plugin
119 127
120 const settings = await PluginManager.Instance.getSettings(plugin.name) 128 const settings = PluginManager.Instance.getSettings(plugin.name)
121 129
122 return res.json({ 130 return res.json({
123 settings 131 settings
diff --git a/server/controllers/themes.ts b/server/controllers/themes.ts
deleted file mode 100644
index 104c285ad..000000000
--- a/server/controllers/themes.ts
+++ /dev/null
@@ -1,30 +0,0 @@
1import * as express from 'express'
2import { join } from 'path'
3import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
4import { serveThemeCSSValidator } from '../middlewares/validators/themes'
5
6const themesRouter = express.Router()
7
8themesRouter.get('/:themeName/:themeVersion/css/:staticEndpoint(*)',
9 serveThemeCSSValidator,
10 serveThemeCSSDirectory
11)
12
13// ---------------------------------------------------------------------------
14
15export {
16 themesRouter
17}
18
19// ---------------------------------------------------------------------------
20
21function serveThemeCSSDirectory (req: express.Request, res: express.Response) {
22 const plugin: RegisteredPlugin = res.locals.registeredPlugin
23 const staticEndpoint = req.params.staticEndpoint
24
25 if (plugin.css.includes(staticEndpoint) === false) {
26 return res.sendStatus(404)
27 }
28
29 return res.sendFile(join(plugin.path, staticEndpoint))
30}
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts
index c5b139378..64818d036 100644
--- a/server/helpers/core-utils.ts
+++ b/server/helpers/core-utils.ts
@@ -3,7 +3,6 @@
3 Useful to avoid circular dependencies. 3 Useful to avoid circular dependencies.
4*/ 4*/
5 5
6import * as bcrypt from 'bcrypt'
7import * as createTorrent from 'create-torrent' 6import * as createTorrent from 'create-torrent'
8import { createHash, HexBase64Latin1Encoding, pseudoRandomBytes } from 'crypto' 7import { createHash, HexBase64Latin1Encoding, pseudoRandomBytes } from 'crypto'
9import { isAbsolute, join } from 'path' 8import { isAbsolute, join } from 'path'
@@ -258,9 +257,6 @@ function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => vo
258const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes) 257const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
259const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey) 258const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
260const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey) 259const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
261const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
262const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
263const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
264const createTorrentPromise = promisify2<string, any, any>(createTorrent) 260const createTorrentPromise = promisify2<string, any, any>(createTorrent)
265const execPromise2 = promisify2<string, any, string>(exec) 261const execPromise2 = promisify2<string, any, string>(exec)
266const execPromise = promisify1<string, string>(exec) 262const execPromise = promisify1<string, string>(exec)
@@ -287,13 +283,11 @@ export {
287 283
288 promisify0, 284 promisify0,
289 promisify1, 285 promisify1,
286 promisify2,
290 287
291 pseudoRandomBytesPromise, 288 pseudoRandomBytesPromise,
292 createPrivateKey, 289 createPrivateKey,
293 getPublicKey, 290 getPublicKey,
294 bcryptComparePromise,
295 bcryptGenSaltPromise,
296 bcryptHashPromise,
297 createTorrentPromise, 291 createTorrentPromise,
298 execPromise2, 292 execPromise2,
299 execPromise 293 execPromise
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index e1a2f9503..f818ce8f1 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -51,7 +51,9 @@ export {
51 51
52function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { 52function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
53 if (!videoChannel) { 53 if (!videoChannel) {
54 `` 54 res.status(404)
55 .json({ error: 'Video channel not found' })
56 .end()
55 57
56 return false 58 return false
57 } 59 }
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 9148df2eb..1424949d0 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -1,12 +1,17 @@
1import { Request } from 'express' 1import { Request } from 'express'
2import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' 2import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
3import { ActorModel } from '../models/activitypub/actor' 3import { ActorModel } from '../models/activitypub/actor'
4import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils' 4import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils'
5import { jsig, jsonld } from './custom-jsonld-signature' 5import { jsig, jsonld } from './custom-jsonld-signature'
6import { logger } from './logger' 6import { logger } from './logger'
7import { cloneDeep } from 'lodash' 7import { cloneDeep } from 'lodash'
8import { createVerify } from 'crypto' 8import { createVerify } from 'crypto'
9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' 9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
10import * as bcrypt from 'bcrypt'
11
12const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
13const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
14const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
10 15
11const httpSignature = require('http-signature') 16const httpSignature = require('http-signature')
12 17
@@ -147,3 +152,5 @@ export {
147 cryptPassword, 152 cryptPassword,
148 signJsonLDObject 153 signJsonLDObject
149} 154}
155
156// ---------------------------------------------------------------------------
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts
index a06add6b8..a1634ded4 100644
--- a/server/middlewares/validators/plugins.ts
+++ b/server/middlewares/validators/plugins.ts
@@ -6,6 +6,7 @@ import { isPluginNameValid, isPluginTypeValid, isPluginVersionValid, isNpmPlugin
6import { PluginManager } from '../../lib/plugins/plugin-manager' 6import { PluginManager } from '../../lib/plugins/plugin-manager'
7import { isBooleanValid, isSafePath } from '../../helpers/custom-validators/misc' 7import { isBooleanValid, isSafePath } from '../../helpers/custom-validators/misc'
8import { PluginModel } from '../../models/server/plugin' 8import { PluginModel } from '../../models/server/plugin'
9import { InstallPlugin } from '../../../shared/models/plugins/install-plugin.model'
9 10
10const servePluginStaticDirectoryValidator = [ 11const servePluginStaticDirectoryValidator = [
11 param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'), 12 param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name'),
@@ -48,13 +49,25 @@ const listPluginsValidator = [
48] 49]
49 50
50const installPluginValidator = [ 51const installPluginValidator = [
51 body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'), 52 body('npmName')
53 .optional()
54 .custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
55 body('path')
56 .optional()
57 .custom(isSafePath).withMessage('Should have a valid safe path'),
52 58
53 (req: express.Request, res: express.Response, next: express.NextFunction) => { 59 (req: express.Request, res: express.Response, next: express.NextFunction) => {
54 logger.debug('Checking installPluginValidator parameters', { parameters: req.body }) 60 logger.debug('Checking installPluginValidator parameters', { parameters: req.body })
55 61
56 if (areValidationErrors(req, res)) return 62 if (areValidationErrors(req, res)) return
57 63
64 const body: InstallPlugin = req.body
65 if (!body.path && !body.npmName) {
66 return res.status(400)
67 .json({ error: 'Should have either a npmName or a path' })
68 .end()
69 }
70
58 return next() 71 return next()
59 } 72 }
60] 73]
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts
index 60abaec65..226c08342 100644
--- a/server/models/server/plugin.ts
+++ b/server/models/server/plugin.ts
@@ -142,15 +142,17 @@ export class PluginModel extends Model<PluginModel> {
142 count: number, 142 count: number,
143 sort: string 143 sort: string
144 }) { 144 }) {
145 const { uninstalled = false } = options
145 const query: FindAndCountOptions = { 146 const query: FindAndCountOptions = {
146 offset: options.start, 147 offset: options.start,
147 limit: options.count, 148 limit: options.count,
148 order: getSort(options.sort), 149 order: getSort(options.sort),
149 where: {} 150 where: {
151 uninstalled
152 }
150 } 153 }
151 154
152 if (options.type) query.where['type'] = options.type 155 if (options.type) query.where['type'] = options.type
153 if (options.uninstalled) query.where['uninstalled'] = options.uninstalled
154 156
155 return PluginModel 157 return PluginModel
156 .findAndCountAll(query) 158 .findAndCountAll(query)
diff --git a/server/tests/api/search/search-activitypub-video-channels.ts b/server/tests/api/search/search-activitypub-video-channels.ts
index 8a008b8c6..d5f0a5457 100644
--- a/server/tests/api/search/search-activitypub-video-channels.ts
+++ b/server/tests/api/search/search-activitypub-video-channels.ts
@@ -67,6 +67,8 @@ describe('Test ActivityPub video channels search', function () {
67 }) 67 })
68 68
69 it('Should not find a remote video channel', async function () { 69 it('Should not find a remote video channel', async function () {
70 this.timeout(15000)
71
70 { 72 {
71 const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server3' 73 const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server3'
72 const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken) 74 const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken)
diff --git a/server/tools/cli.ts b/server/tools/cli.ts
index 2eec51aa4..67755022c 100644
--- a/server/tools/cli.ts
+++ b/server/tools/cli.ts
@@ -1,7 +1,8 @@
1import { Netrc } from 'netrc-parser' 1import { Netrc } from 'netrc-parser'
2import { getAppNumber, isTestInstance } from '../helpers/core-utils' 2import { getAppNumber, isTestInstance } from '../helpers/core-utils'
3import { join } from 'path' 3import { join } from 'path'
4import { getVideoChannel, root } from '../../shared/extra-utils' 4import { root } from '../../shared/extra-utils/miscs/miscs'
5import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels'
5import { Command } from 'commander' 6import { Command } from 'commander'
6import { VideoChannel, VideoPrivacy } from '../../shared/models/videos' 7import { VideoChannel, VideoPrivacy } from '../../shared/models/videos'
7 8
@@ -64,7 +65,11 @@ function deleteSettings () {
64 }) 65 })
65} 66}
66 67
67function getRemoteObjectOrDie (program: any, settings: Settings, netrc: Netrc) { 68function getRemoteObjectOrDie (
69 program: any,
70 settings: Settings,
71 netrc: Netrc
72): { url: string, username: string, password: string } {
68 if (!program['url'] || !program['username'] || !program['password']) { 73 if (!program['url'] || !program['username'] || !program['password']) {
69 // No remote and we don't have program parameters: quit 74 // No remote and we don't have program parameters: quit
70 if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) { 75 if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) {
@@ -161,6 +166,13 @@ async function buildVideoAttributesFromCommander (url: string, command: Command,
161 return videoAttributes 166 return videoAttributes
162} 167}
163 168
169function getServerCredentials (program: any) {
170 return Promise.all([ getSettings(), getNetrc() ])
171 .then(([ settings, netrc ]) => {
172 return getRemoteObjectOrDie(program, settings, netrc)
173 })
174}
175
164// --------------------------------------------------------------------------- 176// ---------------------------------------------------------------------------
165 177
166export { 178export {
@@ -172,6 +184,8 @@ export {
172 writeSettings, 184 writeSettings,
173 deleteSettings, 185 deleteSettings,
174 186
187 getServerCredentials,
188
175 buildCommonVideoOptions, 189 buildCommonVideoOptions,
176 buildVideoAttributesFromCommander 190 buildVideoAttributesFromCommander
177} 191}
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts
index 1035d664a..d4ad56e47 100644
--- a/server/tools/peertube-auth.ts
+++ b/server/tools/peertube-auth.ts
@@ -1,8 +1,8 @@
1import * as program from 'commander' 1import * as program from 'commander'
2import * as prompt from 'prompt' 2import * as prompt from 'prompt'
3import { getSettings, writeSettings, getNetrc } from './cli' 3import { getNetrc, getSettings, writeSettings } from './cli'
4import { isHostValid } from '../helpers/custom-validators/servers'
5import { isUserUsernameValid } from '../helpers/custom-validators/users' 4import { isUserUsernameValid } from '../helpers/custom-validators/users'
5import { getAccessToken, login } from '../../shared/extra-utils'
6 6
7const Table = require('cli-table') 7const Table = require('cli-table')
8 8
@@ -76,6 +76,14 @@ program
76 } 76 }
77 } 77 }
78 }, async (_, result) => { 78 }, async (_, result) => {
79 // Check credentials
80 try {
81 await getAccessToken(result.url, result.username, result.password)
82 } catch (err) {
83 console.error(err.message)
84 process.exit(-1)
85 }
86
79 await setInstance(result.url, result.username, result.password, program['default']) 87 await setInstance(result.url, result.username, result.password, program['default'])
80 88
81 process.exit(0) 89 process.exit(0)
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts
index d7bb00e02..1f0350442 100644
--- a/server/tools/peertube-import-videos.ts
+++ b/server/tools/peertube-import-videos.ts
@@ -11,7 +11,7 @@ import * as prompt from 'prompt'
11import { remove } from 'fs-extra' 11import { remove } from 'fs-extra'
12import { sha256 } from '../helpers/core-utils' 12import { sha256 } from '../helpers/core-utils'
13import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' 13import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl'
14import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getNetrc, getRemoteObjectOrDie, getSettings } from './cli' 14import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
15 15
16type UserInfo = { 16type UserInfo = {
17 username: string 17 username: string
@@ -36,27 +36,25 @@ command
36 .option('-v, --verbose', 'Verbose mode') 36 .option('-v, --verbose', 'Verbose mode')
37 .parse(process.argv) 37 .parse(process.argv)
38 38
39Promise.all([ getSettings(), getNetrc() ]) 39getServerCredentials(command)
40 .then(([ settings, netrc ]) => { 40 .then(({ url, username, password }) => {
41 const { url, username, password } = getRemoteObjectOrDie(program, settings, netrc) 41 if (!program[ 'targetUrl' ]) {
42 console.error('--targetUrl field is required.')
42 43
43 if (!program[ 'targetUrl' ]) { 44 process.exit(-1)
44 console.error('--targetUrl field is required.') 45 }
45
46 process.exit(-1)
47 }
48 46
49 removeEndSlashes(url) 47 removeEndSlashes(url)
50 removeEndSlashes(program[ 'targetUrl' ]) 48 removeEndSlashes(program[ 'targetUrl' ])
51 49
52 const user = { username, password } 50 const user = { username, password }
53 51
54 run(url, user) 52 run(url, user)
55 .catch(err => { 53 .catch(err => {
56 console.error(err) 54 console.error(err)
57 process.exit(-1) 55 process.exit(-1)
58 }) 56 })
59 }) 57 })
60 58
61async function run (url: string, user: UserInfo) { 59async function run (url: string, user: UserInfo) {
62 if (!user.password) { 60 if (!user.password) {
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts
new file mode 100644
index 000000000..d5e024383
--- /dev/null
+++ b/server/tools/peertube-plugins.ts
@@ -0,0 +1,162 @@
1import * as program from 'commander'
2import { PluginType } from '../../shared/models/plugins/plugin.type'
3import { getAccessToken } from '../../shared/extra-utils/users/login'
4import { getMyUserInformation } from '../../shared/extra-utils/users/users'
5import { installPlugin, listPlugins, uninstallPlugin } from '../../shared/extra-utils/server/plugins'
6import { getServerCredentials } from './cli'
7import { User, UserRole } from '../../shared/models/users'
8import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model'
9import { isAbsolute } from 'path'
10
11const Table = require('cli-table')
12
13program
14 .name('plugins')
15 .usage('[command] [options]')
16
17program
18 .command('list')
19 .description('List installed plugins')
20 .option('-u, --url <url>', 'Server url')
21 .option('-U, --username <username>', 'Username')
22 .option('-p, --password <token>', 'Password')
23 .option('-t, --only-themes', 'List themes only')
24 .option('-P, --only-plugins', 'List plugins only')
25 .action(() => pluginsListCLI())
26
27program
28 .command('install')
29 .description('Install a plugin or a theme')
30 .option('-u, --url <url>', 'Server url')
31 .option('-U, --username <username>', 'Username')
32 .option('-p, --password <token>', 'Password')
33 .option('-P --path <path>', 'Install from a path')
34 .option('-n, --npm-name <npmName>', 'Install from npm')
35 .action((options) => installPluginCLI(options))
36
37program
38 .command('uninstall')
39 .description('Uninstall a plugin or a theme')
40 .option('-u, --url <url>', 'Server url')
41 .option('-U, --username <username>', 'Username')
42 .option('-p, --password <token>', 'Password')
43 .option('-n, --npm-name <npmName>', 'NPM plugin/theme name')
44 .action(options => uninstallPluginCLI(options))
45
46if (!process.argv.slice(2).length) {
47 program.outputHelp()
48}
49
50program.parse(process.argv)
51
52// ----------------------------------------------------------------------------
53
54async function pluginsListCLI () {
55 const { url, username, password } = await getServerCredentials(program)
56 const accessToken = await getAdminTokenOrDie(url, username, password)
57
58 let type: PluginType
59 if (program['onlyThemes']) type = PluginType.THEME
60 if (program['onlyPlugins']) type = PluginType.PLUGIN
61
62 const res = await listPlugins({
63 url,
64 accessToken,
65 start: 0,
66 count: 100,
67 sort: 'name',
68 type
69 })
70 const plugins: PeerTubePlugin[] = res.body.data
71
72 const table = new Table({
73 head: ['name', 'version', 'homepage'],
74 colWidths: [ 50, 10, 50 ]
75 })
76
77 for (const plugin of plugins) {
78 const npmName = plugin.type === PluginType.PLUGIN
79 ? 'peertube-plugin-' + plugin.name
80 : 'peertube-theme-' + plugin.name
81
82 table.push([
83 npmName,
84 plugin.version,
85 plugin.homepage
86 ])
87 }
88
89 console.log(table.toString())
90 process.exit(0)
91}
92
93async function installPluginCLI (options: any) {
94 if (!options['path'] && !options['npmName']) {
95 console.error('You need to specify the npm name or the path of the plugin you want to install.\n')
96 program.outputHelp()
97 process.exit(-1)
98 }
99
100 if (options['path'] && !isAbsolute(options['path'])) {
101 console.error('Path should be absolute.')
102 process.exit(-1)
103 }
104
105 const { url, username, password } = await getServerCredentials(options)
106 const accessToken = await getAdminTokenOrDie(url, username, password)
107
108 try {
109 await installPlugin({
110 url,
111 accessToken,
112 npmName: options['npmName'],
113 path: options['path']
114 })
115 } catch (err) {
116 console.error('Cannot install plugin.', err)
117 process.exit(-1)
118 return
119 }
120
121 console.log('Plugin installed.')
122 process.exit(0)
123}
124
125async function uninstallPluginCLI (options: any) {
126 if (!options['npmName']) {
127 console.error('You need to specify the npm name of the plugin/theme you want to uninstall.\n')
128 program.outputHelp()
129 process.exit(-1)
130 }
131
132 const { url, username, password } = await getServerCredentials(options)
133 const accessToken = await getAdminTokenOrDie(url, username, password)
134
135 try {
136 await uninstallPlugin({
137 url,
138 accessToken,
139 npmName: options[ 'npmName' ]
140 })
141 } catch (err) {
142 console.error('Cannot uninstall plugin.', err)
143 process.exit(-1)
144 return
145 }
146
147 console.log('Plugin uninstalled.')
148 process.exit(0)
149}
150
151async function getAdminTokenOrDie (url: string, username: string, password: string) {
152 const accessToken = await getAccessToken(url, username, password)
153 const resMe = await getMyUserInformation(url, accessToken)
154 const me: User = resMe.body
155
156 if (me.role !== UserRole.ADMINISTRATOR) {
157 console.error('Cannot list plugins if you are not administrator.')
158 process.exit(-1)
159 }
160
161 return accessToken
162}
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts
index c00205e8f..d9f9a8ead 100644
--- a/server/tools/peertube-upload.ts
+++ b/server/tools/peertube-upload.ts
@@ -1,9 +1,9 @@
1import * as program from 'commander' 1import * as program from 'commander'
2import { access, constants } from 'fs-extra' 2import { access, constants } from 'fs-extra'
3import { isAbsolute } from 'path' 3import { isAbsolute } from 'path'
4import { getClient, login } from '../../shared/extra-utils' 4import { getAccessToken } from '../../shared/extra-utils'
5import { uploadVideo } from '../../shared/extra-utils/' 5import { uploadVideo } from '../../shared/extra-utils/'
6import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getNetrc, getRemoteObjectOrDie, getSettings } from './cli' 6import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
7 7
8let command = program 8let command = program
9 .name('upload') 9 .name('upload')
@@ -11,7 +11,6 @@ let command = program
11command = buildCommonVideoOptions(command) 11command = buildCommonVideoOptions(command)
12 12
13command 13command
14
15 .option('-u, --url <url>', 'Server url') 14 .option('-u, --url <url>', 'Server url')
16 .option('-U, --username <username>', 'Username') 15 .option('-U, --username <username>', 'Username')
17 .option('-p, --password <token>', 'Password') 16 .option('-p, --password <token>', 'Password')
@@ -20,44 +19,28 @@ command
20 .option('-f, --file <file>', 'Video absolute file path') 19 .option('-f, --file <file>', 'Video absolute file path')
21 .parse(process.argv) 20 .parse(process.argv)
22 21
23Promise.all([ getSettings(), getNetrc() ]) 22getServerCredentials(command)
24 .then(([ settings, netrc ]) => { 23 .then(({ url, username, password }) => {
25 const { url, username, password } = getRemoteObjectOrDie(program, settings, netrc) 24 if (!program[ 'videoName' ] || !program[ 'file' ]) {
26 25 if (!program[ 'videoName' ]) console.error('--video-name is required.')
27 if (!program[ 'videoName' ] || !program[ 'file' ]) { 26 if (!program[ 'file' ]) console.error('--file is required.')
28 if (!program[ 'videoName' ]) console.error('--video-name is required.')
29 if (!program[ 'file' ]) console.error('--file is required.')
30 27
31 process.exit(-1) 28 process.exit(-1)
32 } 29 }
33 30
34 if (isAbsolute(program[ 'file' ]) === false) { 31 if (isAbsolute(program[ 'file' ]) === false) {
35 console.error('File path should be absolute.') 32 console.error('File path should be absolute.')
36 process.exit(-1) 33 process.exit(-1)
37 } 34 }
38 35
39 run(url, username, password).catch(err => { 36 run(url, username, password).catch(err => {
40 console.error(err) 37 console.error(err)
41 process.exit(-1) 38 process.exit(-1)
42 }) 39 })
43 }) 40 })
44 41
45async function run (url: string, username: string, password: string) { 42async function run (url: string, username: string, password: string) {
46 const resClient = await getClient(url) 43 const accessToken = await getAccessToken(url, username, password)
47 const client = {
48 id: resClient.body.client_id,
49 secret: resClient.body.client_secret
50 }
51
52 const user = { username, password }
53
54 let accessToken: string
55 try {
56 const res = await login(url, client, user)
57 accessToken = res.body.access_token
58 } catch (err) {
59 throw new Error('Cannot authenticate. Please check your username/password.')
60 }
61 44
62 await access(program[ 'file' ], constants.F_OK) 45 await access(program[ 'file' ], constants.F_OK)
63 46
diff --git a/server/tools/peertube.ts b/server/tools/peertube.ts
index daa5586c3..e79a7e041 100644
--- a/server/tools/peertube.ts
+++ b/server/tools/peertube.ts
@@ -18,13 +18,10 @@ program
18 .command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token') 18 .command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token')
19 .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') 19 .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w')
20 .command('repl', 'initiate a REPL to access internals') 20 .command('repl', 'initiate a REPL to access internals')
21 .command('plugins [action]', 'manage plugins on a local instance').alias('p')
21 22
22/* Not Yet Implemented */ 23/* Not Yet Implemented */
23program 24program
24 .command('plugins [action]',
25 'manage plugins on a local instance',
26 { noHelp: true } as program.CommandOptions
27 ).alias('p')
28 .command('diagnostic [action]', 25 .command('diagnostic [action]',
29 'like couple therapy, but for your instance', 26 'like couple therapy, but for your instance',
30 { noHelp: true } as program.CommandOptions 27 { noHelp: true } as program.CommandOptions