diff options
-rwxr-xr-x | scripts/update-host.ts | 141 | ||||
-rw-r--r-- | server.ts | 11 | ||||
-rw-r--r-- | server/initializers/checker.ts | 25 | ||||
-rw-r--r-- | server/tests/cli/update-host.ts | 65 | ||||
-rw-r--r-- | support/doc/tools.md | 9 | ||||
-rw-r--r-- | support/doc/translation.md | 5 |
6 files changed, 222 insertions, 34 deletions
diff --git a/scripts/update-host.ts b/scripts/update-host.ts index ed8b999a9..1dc19664d 100755 --- a/scripts/update-host.ts +++ b/scripts/update-host.ts | |||
@@ -1,35 +1,126 @@ | |||
1 | import { getServerActor } from '../server/helpers/utils' | 1 | import { CONFIG, initDatabaseModels } from '../server/initializers' |
2 | import { initDatabaseModels } from '../server/initializers' | ||
3 | import { ActorFollowModel } from '../server/models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../server/models/activitypub/actor-follow' |
4 | import { VideoModel } from '../server/models/video/video' | 3 | import { VideoModel } from '../server/models/video/video' |
4 | import { ActorModel } from '../server/models/activitypub/actor' | ||
5 | import { | ||
6 | getAccountActivityPubUrl, | ||
7 | getAnnounceActivityPubUrl, | ||
8 | getVideoActivityPubUrl, getVideoChannelActivityPubUrl, | ||
9 | getVideoCommentActivityPubUrl | ||
10 | } from '../server/lib/activitypub' | ||
11 | import { VideoShareModel } from '../server/models/video/video-share' | ||
12 | import { VideoCommentModel } from '../server/models/video/video-comment' | ||
13 | import { getServerActor } from '../server/helpers/utils' | ||
14 | import { AccountModel } from '../server/models/account/account' | ||
15 | import { VideoChannelModel } from '../server/models/video/video-channel' | ||
5 | 16 | ||
6 | initDatabaseModels(true) | 17 | run() |
7 | .then(() => { | 18 | .then(() => process.exit(0)) |
8 | return getServerActor() | 19 | .catch(err => { |
9 | }) | 20 | console.error(err) |
10 | .then(serverAccount => { | 21 | process.exit(-1) |
11 | return ActorFollowModel.listAcceptedFollowingUrlsForApi([ serverAccount.id ], undefined) | ||
12 | }) | ||
13 | .then(res => { | ||
14 | return res.total > 0 | ||
15 | }) | 22 | }) |
16 | .then(hasFollowing => { | 23 | |
24 | async function run () { | ||
25 | await initDatabaseModels(true) | ||
26 | |||
27 | const serverAccount = await getServerActor() | ||
28 | |||
29 | { | ||
30 | const res = await ActorFollowModel.listAcceptedFollowingUrlsForApi([ serverAccount.id ], undefined) | ||
31 | const hasFollowing = res.total > 0 | ||
32 | |||
17 | if (hasFollowing === true) { | 33 | if (hasFollowing === true) { |
18 | console.log('Cannot update host because you follow other servers!') | 34 | throw new Error('Cannot update host because you follow other servers!') |
19 | process.exit(-1) | ||
20 | } | 35 | } |
36 | } | ||
21 | 37 | ||
22 | console.log('Updating torrent files.') | 38 | console.log('Updating actors.') |
23 | return VideoModel.list() | 39 | |
24 | }) | 40 | const actors: ActorModel[] = await ActorModel.unscoped().findAll({ |
25 | .then(async videos => { | 41 | include: [ |
26 | for (const video of videos) { | 42 | { |
27 | for (const file of video.VideoFiles) { | 43 | model: VideoChannelModel.unscoped(), |
28 | await video.createTorrentAndSetInfoHash(file) | 44 | required: false |
29 | console.log('Updated video ' + video.uuid) | 45 | }, |
46 | { | ||
47 | model: AccountModel.unscoped(), | ||
48 | required: false | ||
30 | } | 49 | } |
31 | } | 50 | ] |
51 | }) | ||
52 | for (const actor of actors) { | ||
53 | if (actor.isOwned() === false) continue | ||
54 | |||
55 | console.log('Updating actor ' + actor.url) | ||
56 | |||
57 | const newUrl = actor.Account | ||
58 | ? getAccountActivityPubUrl(actor.preferredUsername) | ||
59 | : getVideoChannelActivityPubUrl(actor.preferredUsername) | ||
60 | |||
61 | actor.url = newUrl | ||
62 | actor.inboxUrl = newUrl + '/inbox' | ||
63 | actor.outboxUrl = newUrl + '/outbox' | ||
64 | actor.sharedInboxUrl = CONFIG.WEBSERVER.URL + '/inbox' | ||
65 | actor.followersUrl = newUrl + '/followers' | ||
66 | actor.followingUrl = newUrl + '/following' | ||
67 | |||
68 | await actor.save() | ||
69 | } | ||
70 | |||
71 | console.log('Updating video shares.') | ||
72 | |||
73 | const videoShares: VideoShareModel[] = await VideoShareModel.findAll({ | ||
74 | include: [ VideoModel.unscoped(), ActorModel.unscoped() ] | ||
32 | }) | 75 | }) |
33 | .then(() => { | 76 | for (const videoShare of videoShares) { |
34 | process.exit(0) | 77 | if (videoShare.Video.isOwned() === false) continue |
78 | |||
79 | console.log('Updating video share ' + videoShare.url) | ||
80 | |||
81 | videoShare.url = getAnnounceActivityPubUrl(videoShare.Video.url, videoShare.Actor) | ||
82 | await videoShare.save() | ||
83 | } | ||
84 | |||
85 | console.log('Updating video comments.') | ||
86 | const videoComments: VideoCommentModel[] = await VideoCommentModel.findAll({ | ||
87 | include: [ | ||
88 | { | ||
89 | model: VideoModel.unscoped() | ||
90 | }, | ||
91 | { | ||
92 | model: AccountModel.unscoped(), | ||
93 | include: [ | ||
94 | { | ||
95 | model: ActorModel.unscoped() | ||
96 | } | ||
97 | ] | ||
98 | } | ||
99 | ] | ||
35 | }) | 100 | }) |
101 | for (const comment of videoComments) { | ||
102 | if (comment.isOwned() === false) continue | ||
103 | |||
104 | console.log('Updating comment ' + comment.url) | ||
105 | |||
106 | comment.url = getVideoCommentActivityPubUrl(comment.Video, comment) | ||
107 | await comment.save() | ||
108 | } | ||
109 | |||
110 | console.log('Updating video and torrent files.') | ||
111 | |||
112 | const videos = await VideoModel.list() | ||
113 | for (const video of videos) { | ||
114 | if (video.isOwned() === false) continue | ||
115 | |||
116 | console.log('Updated video ' + video.uuid) | ||
117 | |||
118 | video.url = getVideoActivityPubUrl(video) | ||
119 | await video.save() | ||
120 | |||
121 | for (const file of video.VideoFiles) { | ||
122 | console.log('Updating torrent file %s of video %s.', file.resolution, video.uuid) | ||
123 | await video.createTorrentAndSetInfoHash(file) | ||
124 | } | ||
125 | } | ||
126 | } | ||
@@ -1,6 +1,4 @@ | |||
1 | // FIXME: https://github.com/nodejs/node/pull/16853 | 1 | // FIXME: https://github.com/nodejs/node/pull/16853 |
2 | import { ScheduleVideoUpdateModel } from './server/models/video/schedule-video-update' | ||
3 | |||
4 | require('tls').DEFAULT_ECDH_CURVE = 'auto' | 2 | require('tls').DEFAULT_ECDH_CURVE = 'auto' |
5 | 3 | ||
6 | import { isTestInstance } from './server/helpers/core-utils' | 4 | import { isTestInstance } from './server/helpers/core-utils' |
@@ -26,7 +24,7 @@ process.title = 'peertube' | |||
26 | const app = express() | 24 | const app = express() |
27 | 25 | ||
28 | // ----------- Core checker ----------- | 26 | // ----------- Core checker ----------- |
29 | import { checkMissedConfig, checkFFmpeg, checkConfig } from './server/initializers/checker' | 27 | import { checkMissedConfig, checkFFmpeg, checkConfig, checkActivityPubUrls } from './server/initializers/checker' |
30 | 28 | ||
31 | // Do not use barrels because we don't want to load all modules here (we need to initialize database first) | 29 | // Do not use barrels because we don't want to load all modules here (we need to initialize database first) |
32 | import { logger } from './server/helpers/logger' | 30 | import { logger } from './server/helpers/logger' |
@@ -191,6 +189,13 @@ async function startApplication () { | |||
191 | 189 | ||
192 | await installApplication() | 190 | await installApplication() |
193 | 191 | ||
192 | // Check activity pub urls are valid | ||
193 | checkActivityPubUrls() | ||
194 | .catch(err => { | ||
195 | logger.error('Error in ActivityPub URLs checker.', { err }) | ||
196 | process.exit(-1) | ||
197 | }) | ||
198 | |||
194 | // Email initialization | 199 | // Email initialization |
195 | Emailer.Instance.init() | 200 | Emailer.Instance.init() |
196 | await Emailer.Instance.checkConnectionOrDie() | 201 | await Emailer.Instance.checkConnectionOrDie() |
diff --git a/server/initializers/checker.ts b/server/initializers/checker.ts index 6259c7b6c..d5402098f 100644 --- a/server/initializers/checker.ts +++ b/server/initializers/checker.ts | |||
@@ -3,6 +3,28 @@ import { promisify0 } from '../helpers/core-utils' | |||
3 | import { UserModel } from '../models/account/user' | 3 | import { UserModel } from '../models/account/user' |
4 | import { ApplicationModel } from '../models/application/application' | 4 | import { ApplicationModel } from '../models/application/application' |
5 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 5 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
6 | import { parse } from 'url' | ||
7 | import { CONFIG } from './constants' | ||
8 | import { logger } from '../helpers/logger' | ||
9 | import { getServerActor } from '../helpers/utils' | ||
10 | |||
11 | async function checkActivityPubUrls () { | ||
12 | const actor = await getServerActor() | ||
13 | |||
14 | const parsed = parse(actor.url) | ||
15 | if (CONFIG.WEBSERVER.HOST !== parsed.host) { | ||
16 | const NODE_ENV = config.util.getEnv('NODE_ENV') | ||
17 | const NODE_CONFIG_DIR = config.util.getEnv('NODE_CONFIG_DIR') | ||
18 | |||
19 | logger.warn( | ||
20 | 'It seems PeerTube was started (and created some data) with another domain name. ' + | ||
21 | 'This means you will not be able to federate! ' + | ||
22 | 'Please use %s %s npm run update-host to fix this.', | ||
23 | NODE_CONFIG_DIR ? `NODE_CONFIG_DIR=${NODE_CONFIG_DIR}` : '', | ||
24 | NODE_ENV ? `NODE_ENV=${NODE_ENV}` : '' | ||
25 | ) | ||
26 | } | ||
27 | } | ||
6 | 28 | ||
7 | // Some checks on configuration files | 29 | // Some checks on configuration files |
8 | // Return an error message, or null if everything is okay | 30 | // Return an error message, or null if everything is okay |
@@ -95,5 +117,6 @@ export { | |||
95 | checkMissedConfig, | 117 | checkMissedConfig, |
96 | clientsExist, | 118 | clientsExist, |
97 | usersExist, | 119 | usersExist, |
98 | applicationExist | 120 | applicationExist, |
121 | checkActivityPubUrls | ||
99 | } | 122 | } |
diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts index d0c6d2042..968b7bd7f 100644 --- a/server/tests/cli/update-host.ts +++ b/server/tests/cli/update-host.ts | |||
@@ -3,20 +3,26 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { VideoDetails } from '../../../shared/models/videos' | 5 | import { VideoDetails } from '../../../shared/models/videos' |
6 | import { waitJobs } from '../utils/server/jobs' | ||
7 | import { addVideoCommentThread } from '../utils/videos/video-comments' | ||
6 | import { | 8 | import { |
9 | addVideoChannel, | ||
10 | createUser, | ||
7 | execCLI, | 11 | execCLI, |
8 | flushTests, | 12 | flushTests, |
9 | getEnvCli, | 13 | getEnvCli, |
10 | getVideo, | 14 | getVideo, |
15 | getVideoChannelsList, | ||
11 | getVideosList, | 16 | getVideosList, |
12 | killallServers, | 17 | killallServers, |
18 | makeActivityPubGetRequest, | ||
13 | parseTorrentVideo, | 19 | parseTorrentVideo, |
14 | runServer, | 20 | runServer, |
15 | ServerInfo, | 21 | ServerInfo, |
16 | setAccessTokensToServers, | 22 | setAccessTokensToServers, |
17 | uploadVideo | 23 | uploadVideo |
18 | } from '../utils' | 24 | } from '../utils' |
19 | import { waitJobs } from '../utils/server/jobs' | 25 | import { getAccountsList } from '../utils/users/accounts' |
20 | 26 | ||
21 | const expect = chai.expect | 27 | const expect = chai.expect |
22 | 28 | ||
@@ -39,13 +45,28 @@ describe('Test update host scripts', function () { | |||
39 | 45 | ||
40 | // Upload two videos for our needs | 46 | // Upload two videos for our needs |
41 | const videoAttributes = {} | 47 | const videoAttributes = {} |
48 | const resVideo1 = await uploadVideo(server.url, server.accessToken, videoAttributes) | ||
49 | const video1UUID = resVideo1.body.video.uuid | ||
42 | await uploadVideo(server.url, server.accessToken, videoAttributes) | 50 | await uploadVideo(server.url, server.accessToken, videoAttributes) |
43 | await uploadVideo(server.url, server.accessToken, videoAttributes) | 51 | |
52 | // Create a user | ||
53 | await createUser(server.url, server.accessToken, 'toto', 'coucou') | ||
54 | |||
55 | // Create channel | ||
56 | const videoChannel = { | ||
57 | displayName: 'second video channel', | ||
58 | description: 'super video channel description' | ||
59 | } | ||
60 | await addVideoChannel(server.url, server.accessToken, videoChannel) | ||
61 | |||
62 | // Create comments | ||
63 | const text = 'my super first comment' | ||
64 | await addVideoCommentThread(server.url, server.accessToken, video1UUID, text) | ||
44 | 65 | ||
45 | await waitJobs(server) | 66 | await waitJobs(server) |
46 | }) | 67 | }) |
47 | 68 | ||
48 | it('Should update torrent hosts', async function () { | 69 | it('Should run update host', async function () { |
49 | this.timeout(30000) | 70 | this.timeout(30000) |
50 | 71 | ||
51 | killallServers([ server ]) | 72 | killallServers([ server ]) |
@@ -54,6 +75,44 @@ describe('Test update host scripts', function () { | |||
54 | 75 | ||
55 | const env = getEnvCli(server) | 76 | const env = getEnvCli(server) |
56 | await execCLI(`${env} npm run update-host`) | 77 | await execCLI(`${env} npm run update-host`) |
78 | }) | ||
79 | |||
80 | it('Should have updated videos url', async function () { | ||
81 | const res = await getVideosList(server.url) | ||
82 | expect(res.body.total).to.equal(2) | ||
83 | |||
84 | for (const video of res.body.data) { | ||
85 | const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid) | ||
86 | |||
87 | expect(body.id).to.equal('http://localhost:9002/videos/watch/' + video.uuid) | ||
88 | } | ||
89 | }) | ||
90 | |||
91 | it('Should have updated video channels url', async function () { | ||
92 | const res = await getVideoChannelsList(server.url, 0, 5, '-name') | ||
93 | expect(res.body.total).to.equal(3) | ||
94 | |||
95 | for (const channel of res.body.data) { | ||
96 | const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.uuid) | ||
97 | |||
98 | expect(body.id).to.equal('http://localhost:9002/video-channels/' + channel.uuid) | ||
99 | } | ||
100 | }) | ||
101 | |||
102 | it('Should have update accounts url', async function () { | ||
103 | const res = await getAccountsList(server.url) | ||
104 | expect(res.body.total).to.equal(3) | ||
105 | |||
106 | for (const account of res.body.data) { | ||
107 | const usernameWithDomain = account.name | ||
108 | const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain) | ||
109 | |||
110 | expect(body.id).to.equal('http://localhost:9002/accounts/' + usernameWithDomain) | ||
111 | } | ||
112 | }) | ||
113 | |||
114 | it('Should update torrent hosts', async function () { | ||
115 | this.timeout(30000) | ||
57 | 116 | ||
58 | const res = await getVideosList(server.url) | 117 | const res = await getVideosList(server.url) |
59 | const videos = res.body.data | 118 | const videos = res.body.data |
diff --git a/support/doc/tools.md b/support/doc/tools.md index 0addc0803..cb3944595 100644 --- a/support/doc/tools.md +++ b/support/doc/tools.md | |||
@@ -113,4 +113,13 @@ To delete them (a confirmation will be demanded first): | |||
113 | 113 | ||
114 | ``` | 114 | ``` |
115 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run prune-storage | 115 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run prune-storage |
116 | ``` | ||
117 | |||
118 | ### update-host.js | ||
119 | |||
120 | If you started PeerTube with a domain, and then changed it you will have invalid torrent files and invalid URLs in your database. | ||
121 | To fix this, you have to run: | ||
122 | |||
123 | ``` | ||
124 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run update-host | ||
116 | ``` \ No newline at end of file | 125 | ``` \ No newline at end of file |
diff --git a/support/doc/translation.md b/support/doc/translation.md index 3ad239278..52045083c 100644 --- a/support/doc/translation.md +++ b/support/doc/translation.md | |||
@@ -18,7 +18,8 @@ If you don't see your locale in the platform, please [create an issue](https://g | |||
18 | 18 | ||
19 | There are 4 files: | 19 | There are 4 files: |
20 | * **angular**: contains client strings | 20 | * **angular**: contains client strings |
21 | * **player**: contains player strings | 21 | * **player**: contains player strings. |
22 | * **server**: contains server strings (language, licence...) | 22 | Most of the strings come from VideoJS, so you can help yourself by using [video.js JSON files](https://github.com/videojs/video.js/tree/master/lang) |
23 | * **server**: contains server strings (privacies, licences...) | ||
23 | * **iso639**: contains iso639 (languages) strings used by PeerTube to describe the audio language of a particular video. | 24 | * **iso639**: contains iso639 (languages) strings used by PeerTube to describe the audio language of a particular video. |
24 | It's the reason why these strings should be translated too. There are many strings so do not hesitate to translate only main audio languages. | 25 | It's the reason why these strings should be translated too. There are many strings so do not hesitate to translate only main audio languages. |