diff options
Diffstat (limited to 'server/tools')
-rw-r--r-- | server/tools/cli.ts | 62 | ||||
-rw-r--r-- | server/tools/package.json | 5 | ||||
-rw-r--r-- | server/tools/peertube-auth.ts | 15 | ||||
-rw-r--r-- | server/tools/peertube-import-videos.ts | 87 | ||||
-rw-r--r-- | server/tools/peertube-plugins.ts | 34 | ||||
-rw-r--r-- | server/tools/peertube-redundancy.ts | 197 | ||||
-rw-r--r-- | server/tools/peertube-repl.ts | 41 | ||||
-rw-r--r-- | server/tools/peertube-upload.ts | 21 | ||||
-rw-r--r-- | server/tools/peertube-watch.ts | 10 | ||||
-rw-r--r-- | server/tools/peertube.ts | 24 | ||||
-rw-r--r-- | server/tools/yarn.lock | 23 |
11 files changed, 365 insertions, 154 deletions
diff --git a/server/tools/cli.ts b/server/tools/cli.ts index 58e2445ac..d1a631b69 100644 --- a/server/tools/cli.ts +++ b/server/tools/cli.ts | |||
@@ -6,6 +6,9 @@ import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels' | |||
6 | import { Command } from 'commander' | 6 | import { Command } from 'commander' |
7 | import { VideoChannel, VideoPrivacy } from '../../shared/models/videos' | 7 | import { VideoChannel, VideoPrivacy } from '../../shared/models/videos' |
8 | import { createLogger, format, transports } from 'winston' | 8 | import { createLogger, format, transports } from 'winston' |
9 | import { getMyUserInformation } from '@shared/extra-utils/users/users' | ||
10 | import { User, UserRole } from '@shared/models' | ||
11 | import { getAccessToken } from '@shared/extra-utils/users/login' | ||
9 | 12 | ||
10 | let configName = 'PeerTube/CLI' | 13 | let configName = 'PeerTube/CLI' |
11 | if (isTestInstance()) configName += `-${getAppNumber()}` | 14 | if (isTestInstance()) configName += `-${getAppNumber()}` |
@@ -14,8 +17,21 @@ const config = require('application-config')(configName) | |||
14 | 17 | ||
15 | const version = require('../../../package.json').version | 18 | const version = require('../../../package.json').version |
16 | 19 | ||
20 | async function getAdminTokenOrDie (url: string, username: string, password: string) { | ||
21 | const accessToken = await getAccessToken(url, username, password) | ||
22 | const resMe = await getMyUserInformation(url, accessToken) | ||
23 | const me: User = resMe.body | ||
24 | |||
25 | if (me.role !== UserRole.ADMINISTRATOR) { | ||
26 | console.error('You must be an administrator.') | ||
27 | process.exit(-1) | ||
28 | } | ||
29 | |||
30 | return accessToken | ||
31 | } | ||
32 | |||
17 | interface Settings { | 33 | interface Settings { |
18 | remotes: any[], | 34 | remotes: any[] |
19 | default: number | 35 | default: number |
20 | } | 36 | } |
21 | 37 | ||
@@ -74,9 +90,9 @@ function getRemoteObjectOrDie ( | |||
74 | if (!program['url'] || !program['username'] || !program['password']) { | 90 | if (!program['url'] || !program['username'] || !program['password']) { |
75 | // No remote and we don't have program parameters: quit | 91 | // No remote and we don't have program parameters: quit |
76 | if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) { | 92 | if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) { |
77 | if (!program[ 'url' ]) console.error('--url field is required.') | 93 | if (!program['url']) console.error('--url field is required.') |
78 | if (!program[ 'username' ]) console.error('--username field is required.') | 94 | if (!program['username']) console.error('--username field is required.') |
79 | if (!program[ 'password' ]) console.error('--password field is required.') | 95 | if (!program['password']) console.error('--password field is required.') |
80 | 96 | ||
81 | return process.exit(-1) | 97 | return process.exit(-1) |
82 | } | 98 | } |
@@ -96,9 +112,9 @@ function getRemoteObjectOrDie ( | |||
96 | } | 112 | } |
97 | 113 | ||
98 | return { | 114 | return { |
99 | url: program[ 'url' ], | 115 | url: program['url'], |
100 | username: program[ 'username' ], | 116 | username: program['username'], |
101 | password: program[ 'password' ] | 117 | password: program['password'] |
102 | } | 118 | } |
103 | } | 119 | } |
104 | 120 | ||
@@ -134,8 +150,8 @@ async function buildVideoAttributesFromCommander (url: string, command: Command, | |||
134 | const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {} | 150 | const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {} |
135 | 151 | ||
136 | for (const key of Object.keys(defaultBooleanAttributes)) { | 152 | for (const key of Object.keys(defaultBooleanAttributes)) { |
137 | if (command[ key ] !== undefined) { | 153 | if (command[key] !== undefined) { |
138 | booleanAttributes[key] = command[ key ] | 154 | booleanAttributes[key] = command[key] |
139 | } else if (defaultAttributes[key] !== undefined) { | 155 | } else if (defaultAttributes[key] !== undefined) { |
140 | booleanAttributes[key] = defaultAttributes[key] | 156 | booleanAttributes[key] = defaultAttributes[key] |
141 | } else { | 157 | } else { |
@@ -144,19 +160,19 @@ async function buildVideoAttributesFromCommander (url: string, command: Command, | |||
144 | } | 160 | } |
145 | 161 | ||
146 | const videoAttributes = { | 162 | const videoAttributes = { |
147 | name: command[ 'videoName' ] || defaultAttributes.name, | 163 | name: command['videoName'] || defaultAttributes.name, |
148 | category: command[ 'category' ] || defaultAttributes.category || undefined, | 164 | category: command['category'] || defaultAttributes.category || undefined, |
149 | licence: command[ 'licence' ] || defaultAttributes.licence || undefined, | 165 | licence: command['licence'] || defaultAttributes.licence || undefined, |
150 | language: command[ 'language' ] || defaultAttributes.language || undefined, | 166 | language: command['language'] || defaultAttributes.language || undefined, |
151 | privacy: command[ 'privacy' ] || defaultAttributes.privacy || VideoPrivacy.PUBLIC, | 167 | privacy: command['privacy'] || defaultAttributes.privacy || VideoPrivacy.PUBLIC, |
152 | support: command[ 'support' ] || defaultAttributes.support || undefined, | 168 | support: command['support'] || defaultAttributes.support || undefined, |
153 | description: command[ 'videoDescription' ] || defaultAttributes.description || undefined, | 169 | description: command['videoDescription'] || defaultAttributes.description || undefined, |
154 | tags: command[ 'tags' ] || defaultAttributes.tags || undefined | 170 | tags: command['tags'] || defaultAttributes.tags || undefined |
155 | } | 171 | } |
156 | 172 | ||
157 | Object.assign(videoAttributes, booleanAttributes) | 173 | Object.assign(videoAttributes, booleanAttributes) |
158 | 174 | ||
159 | if (command[ 'channelName' ]) { | 175 | if (command['channelName']) { |
160 | const res = await getVideoChannel(url, command['channelName']) | 176 | const res = await getVideoChannel(url, command['channelName']) |
161 | const videoChannel: VideoChannel = res.body | 177 | const videoChannel: VideoChannel = res.body |
162 | 178 | ||
@@ -172,9 +188,9 @@ async function buildVideoAttributesFromCommander (url: string, command: Command, | |||
172 | 188 | ||
173 | function getServerCredentials (program: any) { | 189 | function getServerCredentials (program: any) { |
174 | return Promise.all([ getSettings(), getNetrc() ]) | 190 | return Promise.all([ getSettings(), getNetrc() ]) |
175 | .then(([ settings, netrc ]) => { | 191 | .then(([ settings, netrc ]) => { |
176 | return getRemoteObjectOrDie(program, settings, netrc) | 192 | return getRemoteObjectOrDie(program, settings, netrc) |
177 | }) | 193 | }) |
178 | } | 194 | } |
179 | 195 | ||
180 | function getLogger (logLevel = 'info') { | 196 | function getLogger (logLevel = 'info') { |
@@ -222,5 +238,7 @@ export { | |||
222 | getServerCredentials, | 238 | getServerCredentials, |
223 | 239 | ||
224 | buildCommonVideoOptions, | 240 | buildCommonVideoOptions, |
225 | buildVideoAttributesFromCommander | 241 | buildVideoAttributesFromCommander, |
242 | |||
243 | getAdminTokenOrDie | ||
226 | } | 244 | } |
diff --git a/server/tools/package.json b/server/tools/package.json index 40959d76e..06ad31cab 100644 --- a/server/tools/package.json +++ b/server/tools/package.json | |||
@@ -4,11 +4,12 @@ | |||
4 | "private": true, | 4 | "private": true, |
5 | "dependencies": { | 5 | "dependencies": { |
6 | "application-config": "^1.0.1", | 6 | "application-config": "^1.0.1", |
7 | "cli-table": "^0.3.1", | 7 | "cli-table3": "^0.5.1", |
8 | "netrc-parser": "^3.1.6", | 8 | "netrc-parser": "^3.1.6", |
9 | "webtorrent-hybrid": "^4.0.1" | 9 | "webtorrent-hybrid": "^4.0.1" |
10 | }, | 10 | }, |
11 | "summon": { | 11 | "summon": { |
12 | "silent": true | 12 | "silent": true |
13 | } | 13 | }, |
14 | "devDependencies": {} | ||
14 | } | 15 | } |
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts index 6597a5c36..6b486e575 100644 --- a/server/tools/peertube-auth.ts +++ b/server/tools/peertube-auth.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | // eslint-disable @typescript-eslint/no-unnecessary-type-assertion | ||
2 | |||
1 | import { registerTSPaths } from '../helpers/register-ts-paths' | 3 | import { registerTSPaths } from '../helpers/register-ts-paths' |
2 | registerTSPaths() | 4 | registerTSPaths() |
3 | 5 | ||
@@ -5,9 +7,8 @@ import * as program from 'commander' | |||
5 | import * as prompt from 'prompt' | 7 | import * as prompt from 'prompt' |
6 | import { getNetrc, getSettings, writeSettings } from './cli' | 8 | import { getNetrc, getSettings, writeSettings } from './cli' |
7 | import { isUserUsernameValid } from '../helpers/custom-validators/users' | 9 | import { isUserUsernameValid } from '../helpers/custom-validators/users' |
8 | import { getAccessToken, login } from '../../shared/extra-utils' | 10 | import { getAccessToken } from '../../shared/extra-utils' |
9 | 11 | import * as CliTable3 from 'cli-table3' | |
10 | const Table = require('cli-table') | ||
11 | 12 | ||
12 | async function delInstance (url: string) { | 13 | async function delInstance (url: string) { |
13 | const [ settings, netrc ] = await Promise.all([ getSettings(), getNetrc() ]) | 14 | const [ settings, netrc ] = await Promise.all([ getSettings(), getNetrc() ]) |
@@ -108,10 +109,10 @@ program | |||
108 | .action(async () => { | 109 | .action(async () => { |
109 | const [ settings, netrc ] = await Promise.all([ getSettings(), getNetrc() ]) | 110 | const [ settings, netrc ] = await Promise.all([ getSettings(), getNetrc() ]) |
110 | 111 | ||
111 | const table = new Table({ | 112 | const table = new CliTable3({ |
112 | head: ['instance', 'login'], | 113 | head: [ 'instance', 'login' ], |
113 | colWidths: [30, 30] | 114 | colWidths: [ 30, 30 ] |
114 | }) | 115 | }) as any |
115 | 116 | ||
116 | settings.remotes.forEach(element => { | 117 | settings.remotes.forEach(element => { |
117 | if (!netrc.machines[element]) return | 118 | if (!netrc.machines[element]) return |
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index eaa792763..0efe87810 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -1,10 +1,6 @@ | |||
1 | import { registerTSPaths } from '../helpers/register-ts-paths' | 1 | import { registerTSPaths } from '../helpers/register-ts-paths' |
2 | |||
3 | registerTSPaths() | 2 | registerTSPaths() |
4 | 3 | ||
5 | // FIXME: https://github.com/nodejs/node/pull/16853 | ||
6 | require('tls').DEFAULT_ECDH_CURVE = 'auto' | ||
7 | |||
8 | import * as program from 'commander' | 4 | import * as program from 'commander' |
9 | import { join } from 'path' | 5 | import { join } from 'path' |
10 | import { doRequestAndSaveToFile } from '../helpers/requests' | 6 | import { doRequestAndSaveToFile } from '../helpers/requests' |
@@ -16,7 +12,7 @@ import { accessSync, constants } from 'fs' | |||
16 | import { remove } from 'fs-extra' | 12 | import { remove } from 'fs-extra' |
17 | import { sha256 } from '../helpers/core-utils' | 13 | import { sha256 } from '../helpers/core-utils' |
18 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' | 14 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' |
19 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials, getLogger } from './cli' | 15 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' |
20 | 16 | ||
21 | type UserInfo = { | 17 | type UserInfo = { |
22 | username: string | 18 | username: string |
@@ -44,30 +40,29 @@ command | |||
44 | .option('-T, --tmpdir <tmpdir>', 'Working directory', __dirname) | 40 | .option('-T, --tmpdir <tmpdir>', 'Working directory', __dirname) |
45 | .parse(process.argv) | 41 | .parse(process.argv) |
46 | 42 | ||
47 | let log = getLogger(program[ 'verbose' ]) | 43 | const log = getLogger(program['verbose']) |
48 | 44 | ||
49 | getServerCredentials(command) | 45 | getServerCredentials(command) |
50 | .then(({ url, username, password }) => { | 46 | .then(({ url, username, password }) => { |
51 | if (!program[ 'targetUrl' ]) { | 47 | if (!program['targetUrl']) { |
52 | exitError('--target-url field is required.') | 48 | exitError('--target-url field is required.') |
53 | } | 49 | } |
54 | 50 | ||
55 | try { | 51 | try { |
56 | accessSync(program[ 'tmpdir' ], constants.R_OK | constants.W_OK) | 52 | accessSync(program['tmpdir'], constants.R_OK | constants.W_OK) |
57 | } catch (e) { | 53 | } catch (e) { |
58 | exitError('--tmpdir %s: directory does not exist or is not accessible', program[ 'tmpdir' ]) | 54 | exitError('--tmpdir %s: directory does not exist or is not accessible', program['tmpdir']) |
59 | } | 55 | } |
60 | 56 | ||
61 | url = normalizeTargetUrl(url) | 57 | url = normalizeTargetUrl(url) |
62 | program[ 'targetUrl' ] = normalizeTargetUrl(program[ 'targetUrl' ]) | 58 | program['targetUrl'] = normalizeTargetUrl(program['targetUrl']) |
63 | 59 | ||
64 | const user = { username, password } | 60 | const user = { username, password } |
65 | 61 | ||
66 | run(url, user) | 62 | run(url, user) |
67 | .catch(err => { | 63 | .catch(err => exitError(err)) |
68 | exitError(err) | ||
69 | }) | ||
70 | }) | 64 | }) |
65 | .catch(err => console.error(err)) | ||
71 | 66 | ||
72 | async function run (url: string, user: UserInfo) { | 67 | async function run (url: string, user: UserInfo) { |
73 | if (!user.password) { | 68 | if (!user.password) { |
@@ -77,7 +72,7 @@ async function run (url: string, user: UserInfo) { | |||
77 | const youtubeDL = await safeGetYoutubeDL() | 72 | const youtubeDL = await safeGetYoutubeDL() |
78 | 73 | ||
79 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] | 74 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] |
80 | youtubeDL.getInfo(program[ 'targetUrl' ], options, processOptions, async (err, info) => { | 75 | youtubeDL.getInfo(program['targetUrl'], options, processOptions, async (err, info) => { |
81 | if (err) { | 76 | if (err) { |
82 | exitError(err.message) | 77 | exitError(err.message) |
83 | } | 78 | } |
@@ -86,10 +81,10 @@ async function run (url: string, user: UserInfo) { | |||
86 | 81 | ||
87 | // Normalize utf8 fields | 82 | // Normalize utf8 fields |
88 | infoArray = [].concat(info) | 83 | infoArray = [].concat(info) |
89 | if (program[ 'first' ]) { | 84 | if (program['first']) { |
90 | infoArray = infoArray.slice(0, program[ 'first' ]) | 85 | infoArray = infoArray.slice(0, program['first']) |
91 | } else if (program[ 'last' ]) { | 86 | } else if (program['last']) { |
92 | infoArray = infoArray.slice(-program[ 'last' ]) | 87 | infoArray = infoArray.slice(-program['last']) |
93 | } | 88 | } |
94 | infoArray = infoArray.map(i => normalizeObject(i)) | 89 | infoArray = infoArray.map(i => normalizeObject(i)) |
95 | 90 | ||
@@ -97,22 +92,22 @@ async function run (url: string, user: UserInfo) { | |||
97 | 92 | ||
98 | for (const info of infoArray) { | 93 | for (const info of infoArray) { |
99 | await processVideo({ | 94 | await processVideo({ |
100 | cwd: program[ 'tmpdir' ], | 95 | cwd: program['tmpdir'], |
101 | url, | 96 | url, |
102 | user, | 97 | user, |
103 | youtubeInfo: info | 98 | youtubeInfo: info |
104 | }) | 99 | }) |
105 | } | 100 | } |
106 | 101 | ||
107 | log.info('Video/s for user %s imported: %s', user.username, program[ 'targetUrl' ]) | 102 | log.info('Video/s for user %s imported: %s', user.username, program['targetUrl']) |
108 | process.exit(0) | 103 | process.exit(0) |
109 | }) | 104 | }) |
110 | } | 105 | } |
111 | 106 | ||
112 | function processVideo (parameters: { | 107 | function processVideo (parameters: { |
113 | cwd: string, | 108 | cwd: string |
114 | url: string, | 109 | url: string |
115 | user: { username: string, password: string }, | 110 | user: { username: string, password: string } |
116 | youtubeInfo: any | 111 | youtubeInfo: any |
117 | }) { | 112 | }) { |
118 | const { youtubeInfo, cwd, url, user } = parameters | 113 | const { youtubeInfo, cwd, url, user } = parameters |
@@ -123,17 +118,17 @@ function processVideo (parameters: { | |||
123 | const videoInfo = await fetchObject(youtubeInfo) | 118 | const videoInfo = await fetchObject(youtubeInfo) |
124 | log.debug('Fetched object.', videoInfo) | 119 | log.debug('Fetched object.', videoInfo) |
125 | 120 | ||
126 | if (program[ 'since' ]) { | 121 | if (program['since']) { |
127 | if (buildOriginallyPublishedAt(videoInfo).getTime() < program[ 'since' ].getTime()) { | 122 | if (buildOriginallyPublishedAt(videoInfo).getTime() < program['since'].getTime()) { |
128 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', | 123 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', |
129 | videoInfo.title, formatDate(program[ 'since' ])) | 124 | videoInfo.title, formatDate(program['since'])) |
130 | return res() | 125 | return res() |
131 | } | 126 | } |
132 | } | 127 | } |
133 | if (program[ 'until' ]) { | 128 | if (program['until']) { |
134 | if (buildOriginallyPublishedAt(videoInfo).getTime() > program[ 'until' ].getTime()) { | 129 | if (buildOriginallyPublishedAt(videoInfo).getTime() > program['until'].getTime()) { |
135 | log.info('Video "%s" has been published after "%s", don\'t upload it.\n', | 130 | log.info('Video "%s" has been published after "%s", don\'t upload it.\n', |
136 | videoInfo.title, formatDate(program[ 'until' ])) | 131 | videoInfo.title, formatDate(program['until'])) |
137 | return res() | 132 | return res() |
138 | } | 133 | } |
139 | } | 134 | } |
@@ -178,11 +173,11 @@ function processVideo (parameters: { | |||
178 | } | 173 | } |
179 | 174 | ||
180 | async function uploadVideoOnPeerTube (parameters: { | 175 | async function uploadVideoOnPeerTube (parameters: { |
181 | videoInfo: any, | 176 | videoInfo: any |
182 | videoPath: string, | 177 | videoPath: string |
183 | cwd: string, | 178 | cwd: string |
184 | url: string, | 179 | url: string |
185 | user: { username: string; password: string } | 180 | user: { username: string, password: string } |
186 | }) { | 181 | }) { |
187 | const { videoInfo, videoPath, cwd, url, user } = parameters | 182 | const { videoInfo, videoPath, cwd, url, user } = parameters |
188 | 183 | ||
@@ -210,9 +205,9 @@ async function uploadVideoOnPeerTube (parameters: { | |||
210 | 205 | ||
211 | const defaultAttributes = { | 206 | const defaultAttributes = { |
212 | name: truncate(videoInfo.title, { | 207 | name: truncate(videoInfo.title, { |
213 | 'length': CONSTRAINTS_FIELDS.VIDEOS.NAME.max, | 208 | length: CONSTRAINTS_FIELDS.VIDEOS.NAME.max, |
214 | 'separator': /,? +/, | 209 | separator: /,? +/, |
215 | 'omission': ' […]' | 210 | omission: ' […]' |
216 | }), | 211 | }), |
217 | category, | 212 | category, |
218 | licence, | 213 | licence, |
@@ -259,7 +254,7 @@ async function uploadVideoOnPeerTube (parameters: { | |||
259 | async function getCategory (categories: string[], url: string) { | 254 | async function getCategory (categories: string[], url: string) { |
260 | if (!categories) return undefined | 255 | if (!categories) return undefined |
261 | 256 | ||
262 | const categoryString = categories[ 0 ] | 257 | const categoryString = categories[0] |
263 | 258 | ||
264 | if (categoryString === 'News & Politics') return 11 | 259 | if (categoryString === 'News & Politics') return 11 |
265 | 260 | ||
@@ -267,7 +262,7 @@ async function getCategory (categories: string[], url: string) { | |||
267 | const categoriesServer = res.body | 262 | const categoriesServer = res.body |
268 | 263 | ||
269 | for (const key of Object.keys(categoriesServer)) { | 264 | for (const key of Object.keys(categoriesServer)) { |
270 | const categoryServer = categoriesServer[ key ] | 265 | const categoryServer = categoriesServer[key] |
271 | if (categoryString.toLowerCase() === categoryServer.toLowerCase()) return parseInt(key, 10) | 266 | if (categoryString.toLowerCase() === categoryServer.toLowerCase()) return parseInt(key, 10) |
272 | } | 267 | } |
273 | 268 | ||
@@ -289,12 +284,12 @@ function normalizeObject (obj: any) { | |||
289 | // Deprecated key | 284 | // Deprecated key |
290 | if (key === 'resolution') continue | 285 | if (key === 'resolution') continue |
291 | 286 | ||
292 | const value = obj[ key ] | 287 | const value = obj[key] |
293 | 288 | ||
294 | if (typeof value === 'string') { | 289 | if (typeof value === 'string') { |
295 | newObj[ key ] = value.normalize() | 290 | newObj[key] = value.normalize() |
296 | } else { | 291 | } else { |
297 | newObj[ key ] = value | 292 | newObj[key] = value |
298 | } | 293 | } |
299 | } | 294 | } |
300 | 295 | ||
@@ -306,7 +301,7 @@ function fetchObject (info: any) { | |||
306 | 301 | ||
307 | return new Promise<any>(async (res, rej) => { | 302 | return new Promise<any>(async (res, rej) => { |
308 | const youtubeDL = await safeGetYoutubeDL() | 303 | const youtubeDL = await safeGetYoutubeDL() |
309 | youtubeDL.getInfo(url, undefined, processOptions, async (err, videoInfo) => { | 304 | youtubeDL.getInfo(url, undefined, processOptions, (err, videoInfo) => { |
310 | if (err) return rej(err) | 305 | if (err) return rej(err) |
311 | 306 | ||
312 | const videoInfoWithUrl = Object.assign(videoInfo, { url }) | 307 | const videoInfoWithUrl = Object.assign(videoInfo, { url }) |
@@ -317,10 +312,10 @@ function fetchObject (info: any) { | |||
317 | 312 | ||
318 | function buildUrl (info: any) { | 313 | function buildUrl (info: any) { |
319 | const webpageUrl = info.webpage_url as string | 314 | const webpageUrl = info.webpage_url as string |
320 | if (webpageUrl && webpageUrl.match(/^https?:\/\//)) return webpageUrl | 315 | if (webpageUrl?.match(/^https?:\/\//)) return webpageUrl |
321 | 316 | ||
322 | const url = info.url as string | 317 | const url = info.url as string |
323 | if (url && url.match(/^https?:\/\//)) return url | 318 | if (url?.match(/^https?:\/\//)) return url |
324 | 319 | ||
325 | // It seems youtube-dl does not return the video url | 320 | // It seems youtube-dl does not return the video url |
326 | return 'https://www.youtube.com/watch?v=' + info.id | 321 | return 'https://www.youtube.com/watch?v=' + info.id |
@@ -388,7 +383,7 @@ function parseDate (dateAsStr: string): Date { | |||
388 | } | 383 | } |
389 | 384 | ||
390 | function formatDate (date: Date): string { | 385 | function formatDate (date: Date): string { |
391 | return date.toISOString().split('T')[ 0 ] | 386 | return date.toISOString().split('T')[0] |
392 | } | 387 | } |
393 | 388 | ||
394 | function exitError (message: string, ...meta: any[]) { | 389 | function exitError (message: string, ...meta: any[]) { |
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts index e40606107..05b75fab2 100644 --- a/server/tools/peertube-plugins.ts +++ b/server/tools/peertube-plugins.ts | |||
@@ -1,17 +1,15 @@ | |||
1 | // eslint-disable @typescript-eslint/no-unnecessary-type-assertion | ||
2 | |||
1 | import { registerTSPaths } from '../helpers/register-ts-paths' | 3 | import { registerTSPaths } from '../helpers/register-ts-paths' |
2 | registerTSPaths() | 4 | registerTSPaths() |
3 | 5 | ||
4 | import * as program from 'commander' | 6 | import * as program from 'commander' |
5 | import { PluginType } from '../../shared/models/plugins/plugin.type' | 7 | import { PluginType } from '../../shared/models/plugins/plugin.type' |
6 | import { getAccessToken } from '../../shared/extra-utils/users/login' | ||
7 | import { getMyUserInformation } from '../../shared/extra-utils/users/users' | ||
8 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' | 8 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' |
9 | import { getServerCredentials } from './cli' | 9 | import { getAdminTokenOrDie, getServerCredentials } from './cli' |
10 | import { User, UserRole } from '../../shared/models/users' | ||
11 | import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model' | 10 | import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model' |
12 | import { isAbsolute } from 'path' | 11 | import { isAbsolute } from 'path' |
13 | 12 | import * as CliTable3 from 'cli-table3' | |
14 | const Table = require('cli-table') | ||
15 | 13 | ||
16 | program | 14 | program |
17 | .name('plugins') | 15 | .name('plugins') |
@@ -82,10 +80,10 @@ async function pluginsListCLI () { | |||
82 | }) | 80 | }) |
83 | const plugins: PeerTubePlugin[] = res.body.data | 81 | const plugins: PeerTubePlugin[] = res.body.data |
84 | 82 | ||
85 | const table = new Table({ | 83 | const table = new CliTable3({ |
86 | head: ['name', 'version', 'homepage'], | 84 | head: [ 'name', 'version', 'homepage' ], |
87 | colWidths: [ 50, 10, 50 ] | 85 | colWidths: [ 50, 10, 50 ] |
88 | }) | 86 | }) as any |
89 | 87 | ||
90 | for (const plugin of plugins) { | 88 | for (const plugin of plugins) { |
91 | const npmName = plugin.type === PluginType.PLUGIN | 89 | const npmName = plugin.type === PluginType.PLUGIN |
@@ -128,7 +126,6 @@ async function installPluginCLI (options: any) { | |||
128 | } catch (err) { | 126 | } catch (err) { |
129 | console.error('Cannot install plugin.', err) | 127 | console.error('Cannot install plugin.', err) |
130 | process.exit(-1) | 128 | process.exit(-1) |
131 | return | ||
132 | } | 129 | } |
133 | 130 | ||
134 | console.log('Plugin installed.') | 131 | console.log('Plugin installed.') |
@@ -160,7 +157,6 @@ async function updatePluginCLI (options: any) { | |||
160 | } catch (err) { | 157 | } catch (err) { |
161 | console.error('Cannot update plugin.', err) | 158 | console.error('Cannot update plugin.', err) |
162 | process.exit(-1) | 159 | process.exit(-1) |
163 | return | ||
164 | } | 160 | } |
165 | 161 | ||
166 | console.log('Plugin updated.') | 162 | console.log('Plugin updated.') |
@@ -181,27 +177,13 @@ async function uninstallPluginCLI (options: any) { | |||
181 | await uninstallPlugin({ | 177 | await uninstallPlugin({ |
182 | url, | 178 | url, |
183 | accessToken, | 179 | accessToken, |
184 | npmName: options[ 'npmName' ] | 180 | npmName: options['npmName'] |
185 | }) | 181 | }) |
186 | } catch (err) { | 182 | } catch (err) { |
187 | console.error('Cannot uninstall plugin.', err) | 183 | console.error('Cannot uninstall plugin.', err) |
188 | process.exit(-1) | 184 | process.exit(-1) |
189 | return | ||
190 | } | 185 | } |
191 | 186 | ||
192 | console.log('Plugin uninstalled.') | 187 | console.log('Plugin uninstalled.') |
193 | process.exit(0) | 188 | process.exit(0) |
194 | } | 189 | } |
195 | |||
196 | async function getAdminTokenOrDie (url: string, username: string, password: string) { | ||
197 | const accessToken = await getAccessToken(url, username, password) | ||
198 | const resMe = await getMyUserInformation(url, accessToken) | ||
199 | const me: User = resMe.body | ||
200 | |||
201 | if (me.role !== UserRole.ADMINISTRATOR) { | ||
202 | console.error('Cannot list plugins if you are not administrator.') | ||
203 | process.exit(-1) | ||
204 | } | ||
205 | |||
206 | return accessToken | ||
207 | } | ||
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts new file mode 100644 index 000000000..1ab58a438 --- /dev/null +++ b/server/tools/peertube-redundancy.ts | |||
@@ -0,0 +1,197 @@ | |||
1 | // eslint-disable @typescript-eslint/no-unnecessary-type-assertion | ||
2 | |||
3 | import { registerTSPaths } from '../helpers/register-ts-paths' | ||
4 | registerTSPaths() | ||
5 | |||
6 | import * as program from 'commander' | ||
7 | import { getAdminTokenOrDie, getServerCredentials } from './cli' | ||
8 | import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' | ||
9 | import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy' | ||
10 | import validator from 'validator' | ||
11 | import * as CliTable3 from 'cli-table3' | ||
12 | import { URL } from 'url' | ||
13 | import { uniq } from 'lodash' | ||
14 | |||
15 | import bytes = require('bytes') | ||
16 | |||
17 | program | ||
18 | .name('plugins') | ||
19 | .usage('[command] [options]') | ||
20 | |||
21 | program | ||
22 | .command('list-remote-redundancies') | ||
23 | .description('List remote redundancies on your videos') | ||
24 | .option('-u, --url <url>', 'Server url') | ||
25 | .option('-U, --username <username>', 'Username') | ||
26 | .option('-p, --password <token>', 'Password') | ||
27 | .action(() => listRedundanciesCLI('my-videos')) | ||
28 | |||
29 | program | ||
30 | .command('list-my-redundancies') | ||
31 | .description('List your redundancies of remote videos') | ||
32 | .option('-u, --url <url>', 'Server url') | ||
33 | .option('-U, --username <username>', 'Username') | ||
34 | .option('-p, --password <token>', 'Password') | ||
35 | .action(() => listRedundanciesCLI('remote-videos')) | ||
36 | |||
37 | program | ||
38 | .command('add') | ||
39 | .description('Duplicate a video in your redundancy system') | ||
40 | .option('-u, --url <url>', 'Server url') | ||
41 | .option('-U, --username <username>', 'Username') | ||
42 | .option('-p, --password <token>', 'Password') | ||
43 | .option('-v, --video <videoId>', 'Video id to duplicate') | ||
44 | .action((options) => addRedundancyCLI(options)) | ||
45 | |||
46 | program | ||
47 | .command('remove') | ||
48 | .description('Remove a video from your redundancies') | ||
49 | .option('-u, --url <url>', 'Server url') | ||
50 | .option('-U, --username <username>', 'Username') | ||
51 | .option('-p, --password <token>', 'Password') | ||
52 | .option('-v, --video <videoId>', 'Video id to remove from redundancies') | ||
53 | .action((options) => removeRedundancyCLI(options)) | ||
54 | |||
55 | if (!process.argv.slice(2).length) { | ||
56 | program.outputHelp() | ||
57 | } | ||
58 | |||
59 | program.parse(process.argv) | ||
60 | |||
61 | // ---------------------------------------------------------------------------- | ||
62 | |||
63 | async function listRedundanciesCLI (target: VideoRedundanciesTarget) { | ||
64 | const { url, username, password } = await getServerCredentials(program) | ||
65 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
66 | |||
67 | const redundancies = await listVideoRedundanciesData(url, accessToken, target) | ||
68 | |||
69 | const table = new CliTable3({ | ||
70 | head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ] | ||
71 | }) as any | ||
72 | |||
73 | for (const redundancy of redundancies) { | ||
74 | const webtorrentFiles = redundancy.redundancies.files | ||
75 | const streamingPlaylists = redundancy.redundancies.streamingPlaylists | ||
76 | |||
77 | let totalSize = '' | ||
78 | if (target === 'remote-videos') { | ||
79 | const tmp = webtorrentFiles.concat(streamingPlaylists) | ||
80 | .reduce((a, b) => a + b.size, 0) | ||
81 | |||
82 | totalSize = bytes(tmp) | ||
83 | } | ||
84 | |||
85 | const instances = uniq( | ||
86 | webtorrentFiles.concat(streamingPlaylists) | ||
87 | .map(r => r.fileUrl) | ||
88 | .map(u => new URL(u).host) | ||
89 | ) | ||
90 | |||
91 | table.push([ | ||
92 | redundancy.id.toString(), | ||
93 | redundancy.name, | ||
94 | redundancy.url, | ||
95 | webtorrentFiles.length, | ||
96 | streamingPlaylists.length, | ||
97 | instances.join('\n'), | ||
98 | totalSize | ||
99 | ]) | ||
100 | } | ||
101 | |||
102 | console.log(table.toString()) | ||
103 | process.exit(0) | ||
104 | } | ||
105 | |||
106 | async function addRedundancyCLI (options: { videoId: number }) { | ||
107 | const { url, username, password } = await getServerCredentials(program) | ||
108 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
109 | |||
110 | if (!options['video'] || validator.isInt('' + options['video']) === false) { | ||
111 | console.error('You need to specify the video id to duplicate and it should be a number.\n') | ||
112 | program.outputHelp() | ||
113 | process.exit(-1) | ||
114 | } | ||
115 | |||
116 | try { | ||
117 | await addVideoRedundancy({ | ||
118 | url, | ||
119 | accessToken, | ||
120 | videoId: options['video'] | ||
121 | }) | ||
122 | |||
123 | console.log('Video will be duplicated by your instance!') | ||
124 | |||
125 | process.exit(0) | ||
126 | } catch (err) { | ||
127 | if (err.message.includes(409)) { | ||
128 | console.error('This video is already duplicated by your instance.') | ||
129 | } else if (err.message.includes(404)) { | ||
130 | console.error('This video id does not exist.') | ||
131 | } else { | ||
132 | console.error(err) | ||
133 | } | ||
134 | |||
135 | process.exit(-1) | ||
136 | } | ||
137 | } | ||
138 | |||
139 | async function removeRedundancyCLI (options: { videoId: number }) { | ||
140 | const { url, username, password } = await getServerCredentials(program) | ||
141 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
142 | |||
143 | if (!options['video'] || validator.isInt('' + options['video']) === false) { | ||
144 | console.error('You need to specify the video id to remove from your redundancies.\n') | ||
145 | program.outputHelp() | ||
146 | process.exit(-1) | ||
147 | } | ||
148 | |||
149 | const videoId = parseInt(options['video'] + '', 10) | ||
150 | |||
151 | let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') | ||
152 | let videoRedundancy = redundancies.find(r => videoId === r.id) | ||
153 | |||
154 | if (!videoRedundancy) { | ||
155 | redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos') | ||
156 | videoRedundancy = redundancies.find(r => videoId === r.id) | ||
157 | } | ||
158 | |||
159 | if (!videoRedundancy) { | ||
160 | console.error('Video redundancy not found.') | ||
161 | process.exit(-1) | ||
162 | } | ||
163 | |||
164 | try { | ||
165 | const ids = videoRedundancy.redundancies.files | ||
166 | .concat(videoRedundancy.redundancies.streamingPlaylists) | ||
167 | .map(r => r.id) | ||
168 | |||
169 | for (const id of ids) { | ||
170 | await removeVideoRedundancy({ | ||
171 | url, | ||
172 | accessToken, | ||
173 | redundancyId: id | ||
174 | }) | ||
175 | } | ||
176 | |||
177 | console.log('Video redundancy removed!') | ||
178 | |||
179 | process.exit(0) | ||
180 | } catch (err) { | ||
181 | console.error(err) | ||
182 | process.exit(-1) | ||
183 | } | ||
184 | } | ||
185 | |||
186 | async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) { | ||
187 | const res = await listVideoRedundancies({ | ||
188 | url, | ||
189 | accessToken, | ||
190 | start: 0, | ||
191 | count: 100, | ||
192 | sort: 'name', | ||
193 | target | ||
194 | }) | ||
195 | |||
196 | return res.body.data as VideoRedundancy[] | ||
197 | } | ||
diff --git a/server/tools/peertube-repl.ts b/server/tools/peertube-repl.ts index ab6e215d9..7c936ae0d 100644 --- a/server/tools/peertube-repl.ts +++ b/server/tools/peertube-repl.ts | |||
@@ -1,6 +1,4 @@ | |||
1 | import { registerTSPaths } from '../helpers/register-ts-paths' | 1 | import { registerTSPaths } from '../helpers/register-ts-paths' |
2 | registerTSPaths() | ||
3 | |||
4 | import * as repl from 'repl' | 2 | import * as repl from 'repl' |
5 | import * as path from 'path' | 3 | import * as path from 'path' |
6 | import * as _ from 'lodash' | 4 | import * as _ from 'lodash' |
@@ -23,6 +21,8 @@ import * as signupUtils from '../helpers/signup' | |||
23 | import * as utils from '../helpers/utils' | 21 | import * as utils from '../helpers/utils' |
24 | import * as YoutubeDLUtils from '../helpers/youtube-dl' | 22 | import * as YoutubeDLUtils from '../helpers/youtube-dl' |
25 | 23 | ||
24 | registerTSPaths() | ||
25 | |||
26 | const start = async () => { | 26 | const start = async () => { |
27 | await initDatabaseModels(true) | 27 | await initDatabaseModels(true) |
28 | 28 | ||
@@ -31,22 +31,39 @@ const start = async () => { | |||
31 | const initContext = (replServer) => { | 31 | const initContext = (replServer) => { |
32 | return (context) => { | 32 | return (context) => { |
33 | const properties = { | 33 | const properties = { |
34 | context, repl: replServer, env: process.env, | 34 | context, |
35 | lodash: _, path, | 35 | repl: replServer, |
36 | uuidv1, uuidv3, uuidv4, uuidv5, | 36 | env: process.env, |
37 | cli, logger, constants, | 37 | lodash: _, |
38 | Sequelize, sequelizeTypescript, modelsUtils, | 38 | path, |
39 | models: sequelizeTypescript.models, transaction: sequelizeTypescript.transaction, | 39 | uuidv1, |
40 | query: sequelizeTypescript.query, queryInterface: sequelizeTypescript.getQueryInterface(), | 40 | uuidv3, |
41 | uuidv4, | ||
42 | uuidv5, | ||
43 | cli, | ||
44 | logger, | ||
45 | constants, | ||
46 | Sequelize, | ||
47 | sequelizeTypescript, | ||
48 | modelsUtils, | ||
49 | models: sequelizeTypescript.models, | ||
50 | transaction: sequelizeTypescript.transaction, | ||
51 | query: sequelizeTypescript.query, | ||
52 | queryInterface: sequelizeTypescript.getQueryInterface(), | ||
41 | YoutubeDL, | 53 | YoutubeDL, |
42 | coreUtils, ffmpegUtils, peertubeCryptoUtils, signupUtils, utils, YoutubeDLUtils | 54 | coreUtils, |
55 | ffmpegUtils, | ||
56 | peertubeCryptoUtils, | ||
57 | signupUtils, | ||
58 | utils, | ||
59 | YoutubeDLUtils | ||
43 | } | 60 | } |
44 | 61 | ||
45 | for (let prop in properties) { | 62 | for (const prop in properties) { |
46 | Object.defineProperty(context, prop, { | 63 | Object.defineProperty(context, prop, { |
47 | configurable: false, | 64 | configurable: false, |
48 | enumerable: true, | 65 | enumerable: true, |
49 | value: properties[ prop ] | 66 | value: properties[prop] |
50 | }) | 67 | }) |
51 | } | 68 | } |
52 | } | 69 | } |
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts index f604c9bee..8de952e7b 100644 --- a/server/tools/peertube-upload.ts +++ b/server/tools/peertube-upload.ts | |||
@@ -24,14 +24,14 @@ command | |||
24 | 24 | ||
25 | getServerCredentials(command) | 25 | getServerCredentials(command) |
26 | .then(({ url, username, password }) => { | 26 | .then(({ url, username, password }) => { |
27 | if (!program[ 'videoName' ] || !program[ 'file' ]) { | 27 | if (!program['videoName'] || !program['file']) { |
28 | if (!program[ 'videoName' ]) console.error('--video-name is required.') | 28 | if (!program['videoName']) console.error('--video-name is required.') |
29 | if (!program[ 'file' ]) console.error('--file is required.') | 29 | if (!program['file']) console.error('--file is required.') |
30 | 30 | ||
31 | process.exit(-1) | 31 | process.exit(-1) |
32 | } | 32 | } |
33 | 33 | ||
34 | if (isAbsolute(program[ 'file' ]) === false) { | 34 | if (isAbsolute(program['file']) === false) { |
35 | console.error('File path should be absolute.') | 35 | console.error('File path should be absolute.') |
36 | process.exit(-1) | 36 | process.exit(-1) |
37 | } | 37 | } |
@@ -41,25 +41,26 @@ getServerCredentials(command) | |||
41 | process.exit(-1) | 41 | process.exit(-1) |
42 | }) | 42 | }) |
43 | }) | 43 | }) |
44 | .catch(err => console.error(err)) | ||
44 | 45 | ||
45 | async function run (url: string, username: string, password: string) { | 46 | async function run (url: string, username: string, password: string) { |
46 | const accessToken = await getAccessToken(url, username, password) | 47 | const accessToken = await getAccessToken(url, username, password) |
47 | 48 | ||
48 | await access(program[ 'file' ], constants.F_OK) | 49 | await access(program['file'], constants.F_OK) |
49 | 50 | ||
50 | console.log('Uploading %s video...', program[ 'videoName' ]) | 51 | console.log('Uploading %s video...', program['videoName']) |
51 | 52 | ||
52 | const videoAttributes = await buildVideoAttributesFromCommander(url, program) | 53 | const videoAttributes = await buildVideoAttributesFromCommander(url, program) |
53 | 54 | ||
54 | Object.assign(videoAttributes, { | 55 | Object.assign(videoAttributes, { |
55 | fixture: program[ 'file' ], | 56 | fixture: program['file'], |
56 | thumbnailfile: program[ 'thumbnail' ], | 57 | thumbnailfile: program['thumbnail'], |
57 | previewfile: program[ 'preview' ] | 58 | previewfile: program['preview'] |
58 | }) | 59 | }) |
59 | 60 | ||
60 | try { | 61 | try { |
61 | await uploadVideo(url, accessToken, videoAttributes) | 62 | await uploadVideo(url, accessToken, videoAttributes) |
62 | console.log(`Video ${program[ 'videoName' ]} uploaded.`) | 63 | console.log(`Video ${program['videoName']} uploaded.`) |
63 | process.exit(0) | 64 | process.exit(0) |
64 | } catch (err) { | 65 | } catch (err) { |
65 | console.error(require('util').inspect(err)) | 66 | console.error(require('util').inspect(err)) |
diff --git a/server/tools/peertube-watch.ts b/server/tools/peertube-watch.ts index 9ac1d05f9..5f7d1bb07 100644 --- a/server/tools/peertube-watch.ts +++ b/server/tools/peertube-watch.ts | |||
@@ -29,16 +29,10 @@ program | |||
29 | console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') | 29 | console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') |
30 | console.log() | 30 | console.log() |
31 | }) | 31 | }) |
32 | .action((url, cmd) => { | 32 | .action((url, cmd) => run(url, cmd)) |
33 | run(url, cmd) | ||
34 | .catch(err => { | ||
35 | console.error(err) | ||
36 | process.exit(-1) | ||
37 | }) | ||
38 | }) | ||
39 | .parse(process.argv) | 33 | .parse(process.argv) |
40 | 34 | ||
41 | async function run (url: string, program: any) { | 35 | function run (url: string, program: any) { |
42 | if (!url) { | 36 | if (!url) { |
43 | console.error('<url> positional argument is required.') | 37 | console.error('<url> positional argument is required.') |
44 | process.exit(-1) | 38 | process.exit(-1) |
diff --git a/server/tools/peertube.ts b/server/tools/peertube.ts index fc85c4210..88dd5f7f6 100644 --- a/server/tools/peertube.ts +++ b/server/tools/peertube.ts | |||
@@ -1,13 +1,12 @@ | |||
1 | #!/usr/bin/env node | 1 | #!/usr/bin/env node |
2 | 2 | ||
3 | /* eslint-disable no-useless-escape */ | ||
4 | |||
3 | import { registerTSPaths } from '../helpers/register-ts-paths' | 5 | import { registerTSPaths } from '../helpers/register-ts-paths' |
4 | registerTSPaths() | 6 | registerTSPaths() |
5 | 7 | ||
6 | import * as program from 'commander' | 8 | import * as program from 'commander' |
7 | import { | 9 | import { getSettings, version } from './cli' |
8 | version, | ||
9 | getSettings | ||
10 | } from './cli' | ||
11 | 10 | ||
12 | program | 11 | program |
13 | .version(version, '-v, --version') | 12 | .version(version, '-v, --version') |
@@ -22,17 +21,19 @@ program | |||
22 | .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') | 21 | .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') |
23 | .command('repl', 'initiate a REPL to access internals') | 22 | .command('repl', 'initiate a REPL to access internals') |
24 | .command('plugins [action]', 'manage instance plugins/themes').alias('p') | 23 | .command('plugins [action]', 'manage instance plugins/themes').alias('p') |
24 | .command('redundancy [action]', 'manage instance redundancies').alias('r') | ||
25 | 25 | ||
26 | /* Not Yet Implemented */ | 26 | /* Not Yet Implemented */ |
27 | program | 27 | program |
28 | .command('diagnostic [action]', | 28 | .command( |
29 | 'like couple therapy, but for your instance', | 29 | 'diagnostic [action]', |
30 | { noHelp: true } as program.CommandOptions | 30 | 'like couple therapy, but for your instance', |
31 | ).alias('d') | 31 | { noHelp: true } as program.CommandOptions |
32 | ).alias('d') | ||
32 | .command('admin', | 33 | .command('admin', |
33 | 'manage an instance where you have elevated rights', | 34 | 'manage an instance where you have elevated rights', |
34 | { noHelp: true } as program.CommandOptions | 35 | { noHelp: true } as program.CommandOptions |
35 | ).alias('a') | 36 | ).alias('a') |
36 | 37 | ||
37 | // help on no command | 38 | // help on no command |
38 | if (!process.argv.slice(2).length) { | 39 | if (!process.argv.slice(2).length) { |
@@ -81,3 +82,4 @@ getSettings() | |||
81 | }) | 82 | }) |
82 | .parse(process.argv) | 83 | .parse(process.argv) |
83 | }) | 84 | }) |
85 | .catch(err => console.error(err)) | ||
diff --git a/server/tools/yarn.lock b/server/tools/yarn.lock index 28756cbc2..ccd716a51 100644 --- a/server/tools/yarn.lock +++ b/server/tools/yarn.lock | |||
@@ -347,12 +347,15 @@ chunk-store-stream@^4.0.0: | |||
347 | block-stream2 "^2.0.0" | 347 | block-stream2 "^2.0.0" |
348 | readable-stream "^3.4.0" | 348 | readable-stream "^3.4.0" |
349 | 349 | ||
350 | cli-table@^0.3.1: | 350 | cli-table3@^0.5.1: |
351 | version "0.3.1" | 351 | version "0.5.1" |
352 | resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" | 352 | resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" |
353 | integrity sha1-9TsFJmqLGguTSz0IIebi3FkUriM= | 353 | integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== |
354 | dependencies: | 354 | dependencies: |
355 | colors "1.0.3" | 355 | object-assign "^4.1.0" |
356 | string-width "^2.1.1" | ||
357 | optionalDependencies: | ||
358 | colors "^1.1.2" | ||
356 | 359 | ||
357 | clivas@^0.2.0: | 360 | clivas@^0.2.0: |
358 | version "0.2.0" | 361 | version "0.2.0" |
@@ -364,10 +367,10 @@ code-point-at@^1.0.0: | |||
364 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" | 367 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" |
365 | integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= | 368 | integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= |
366 | 369 | ||
367 | colors@1.0.3: | 370 | colors@^1.1.2: |
368 | version "1.0.3" | 371 | version "1.4.0" |
369 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" | 372 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" |
370 | integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= | 373 | integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== |
371 | 374 | ||
372 | common-tags@^1.8.0: | 375 | common-tags@^1.8.0: |
373 | version "1.8.0" | 376 | version "1.8.0" |
@@ -1609,7 +1612,7 @@ string-width@^1.0.1: | |||
1609 | is-fullwidth-code-point "^1.0.0" | 1612 | is-fullwidth-code-point "^1.0.0" |
1610 | strip-ansi "^3.0.0" | 1613 | strip-ansi "^3.0.0" |
1611 | 1614 | ||
1612 | "string-width@^1.0.2 || 2": | 1615 | "string-width@^1.0.2 || 2", string-width@^2.1.1: |
1613 | version "2.1.1" | 1616 | version "2.1.1" |
1614 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" | 1617 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" |
1615 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== | 1618 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== |