diff options
author | Gérald Niel <gerald.niel@gegeweb.org> | 2018-04-19 07:54:51 +0200 |
---|---|---|
committer | Gérald Niel <gerald.niel@gegeweb.org> | 2018-04-19 07:54:51 +0200 |
commit | 0030284b0df2983914291d6fe83675e2aa892e6a (patch) | |
tree | 2379f6f0c8a5e6ec1cb86d4bc808c7c48a4c4422 | |
parent | ea5cd0fa2cdc7655ed5ecf134dedd52400837ef2 (diff) | |
parent | f55e5a7bf81c2c27db1514273e3366511aabf4ae (diff) | |
download | PeerTube-0030284b0df2983914291d6fe83675e2aa892e6a.tar.gz PeerTube-0030284b0df2983914291d6fe83675e2aa892e6a.tar.zst PeerTube-0030284b0df2983914291d6fe83675e2aa892e6a.zip |
Merge branch 'develop' of framagit.org:chocobozzz/PeerTube into develop
-rw-r--r-- | client/src/app/videos/+video-watch/comment/video-comment.component.scss | 27 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/comment/video-comments.component.scss | 10 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.scss | 2 | ||||
-rw-r--r-- | client/src/assets/player/peertube-player.ts | 3 | ||||
-rw-r--r-- | client/src/assets/player/peertube-videojs-plugin.ts | 35 | ||||
-rw-r--r-- | client/src/assets/player/peertube-videojs-typings.ts | 1 | ||||
-rw-r--r-- | server.ts | 7 | ||||
-rw-r--r-- | server/controllers/api/server/follows.ts | 78 | ||||
-rw-r--r-- | server/initializers/constants.ts | 4 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/activitypub-follow.ts | 68 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/activitypub-http-broadcast.ts | 17 | ||||
-rw-r--r-- | server/lib/job-queue/job-queue.ts | 5 | ||||
-rw-r--r-- | server/tests/utils/server/servers.ts | 2 | ||||
-rw-r--r-- | shared/models/server/job.model.ts | 1 |
14 files changed, 164 insertions, 96 deletions
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.scss b/client/src/app/videos/+video-watch/comment/video-comment.component.scss index 3b0b7eafd..3a3f32b83 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment.component.scss +++ b/client/src/app/videos/+video-watch/comment/video-comment.component.scss | |||
@@ -14,6 +14,8 @@ | |||
14 | 14 | ||
15 | .comment { | 15 | .comment { |
16 | flex-grow: 1; | 16 | flex-grow: 1; |
17 | // Fix word-wrap with flex | ||
18 | min-width: 1px; | ||
17 | 19 | ||
18 | .highlighted-comment { | 20 | .highlighted-comment { |
19 | display: inline-block; | 21 | display: inline-block; |
@@ -44,8 +46,8 @@ | |||
44 | } | 46 | } |
45 | 47 | ||
46 | .comment-html { | 48 | .comment-html { |
47 | word-wrap: initial; | ||
48 | word-break: normal; | 49 | word-break: normal; |
50 | word-wrap: break-word; | ||
49 | text-align: justify; | 51 | text-align: justify; |
50 | 52 | ||
51 | /deep/ a { | 53 | /deep/ a { |
@@ -76,3 +78,26 @@ | |||
76 | } | 78 | } |
77 | } | 79 | } |
78 | } | 80 | } |
81 | |||
82 | // Decrease the space of child comments on small screens | ||
83 | @media screen and (max-width: 1600px) { | ||
84 | .children { | ||
85 | margin-left: -20px; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | @media screen and (max-width: 1200px) { | ||
90 | .children { | ||
91 | margin-left: -30px; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | @media screen and (max-width: 600px) { | ||
96 | .children { | ||
97 | margin-left: -40px; | ||
98 | } | ||
99 | |||
100 | .root-comment { | ||
101 | img { margin-right: 10px; } | ||
102 | } | ||
103 | } \ No newline at end of file | ||
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.scss b/client/src/app/videos/+video-watch/comment/video-comments.component.scss index 7aadc2866..0b8aa1854 100644 --- a/client/src/app/videos/+video-watch/comment/video-comments.component.scss +++ b/client/src/app/videos/+video-watch/comment/video-comments.component.scss | |||
@@ -19,8 +19,8 @@ | |||
19 | font-size: 13px; | 19 | font-size: 13px; |
20 | } | 20 | } |
21 | 21 | ||
22 | .comment-html { | 22 | @media screen and (max-width: 600px) { |
23 | word-wrap: normal; | 23 | .view-replies { |
24 | word-break: normal; | 24 | margin-left: 46px; |
25 | text-align: justify; | 25 | } |
26 | } | 26 | } \ No newline at end of file |
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index 9b7bc7351..d78e5c6a1 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss | |||
@@ -335,7 +335,7 @@ | |||
335 | 335 | ||
336 | .video-actions-rates { | 336 | .video-actions-rates { |
337 | margin-top: 20px; | 337 | margin-top: 20px; |
338 | align-items: left; | 338 | align-items: start; |
339 | 339 | ||
340 | .video-info-likes-dislikes-bar { | 340 | .video-info-likes-dislikes-bar { |
341 | margin-top: 10px; | 341 | margin-top: 10px; |
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts index e8a258065..f02fe5d75 100644 --- a/client/src/assets/player/peertube-player.ts +++ b/client/src/assets/player/peertube-player.ts | |||
@@ -27,11 +27,12 @@ function getVideojsOptions (options: { | |||
27 | const videojsOptions = { | 27 | const videojsOptions = { |
28 | controls: true, | 28 | controls: true, |
29 | poster: options.poster, | 29 | poster: options.poster, |
30 | autoplay: options.autoplay, | 30 | autoplay: false, |
31 | inactivityTimeout: options.inactivityTimeout, | 31 | inactivityTimeout: options.inactivityTimeout, |
32 | playbackRates: [ 0.5, 1, 1.5, 2 ], | 32 | playbackRates: [ 0.5, 1, 1.5, 2 ], |
33 | plugins: { | 33 | plugins: { |
34 | peertube: { | 34 | peertube: { |
35 | autoplay: options.autoplay, // Use peertube plugin autoplay because we get the file by webtorrent | ||
35 | videoFiles: options.videoFiles, | 36 | videoFiles: options.videoFiles, |
36 | playerElement: options.playerElement, | 37 | playerElement: options.playerElement, |
37 | videoViewUrl: options.videoViewUrl, | 38 | videoViewUrl: options.videoViewUrl, |
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 60c291a50..290d88724 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts | |||
@@ -68,9 +68,7 @@ class PeerTubePlugin extends Plugin { | |||
68 | constructor (player: videojs.Player, options: PeertubePluginOptions) { | 68 | constructor (player: videojs.Player, options: PeertubePluginOptions) { |
69 | super(player, options) | 69 | super(player, options) |
70 | 70 | ||
71 | // Fix canplay event on google chrome by disabling default videojs autoplay | 71 | this.autoplay = options.autoplay |
72 | this.autoplay = this.player.options_.autoplay | ||
73 | this.player.options_.autoplay = false | ||
74 | 72 | ||
75 | this.startTime = options.startTime | 73 | this.startTime = options.startTime |
76 | this.videoFiles = options.videoFiles | 74 | this.videoFiles = options.videoFiles |
@@ -190,12 +188,7 @@ class PeerTubePlugin extends Plugin { | |||
190 | 188 | ||
191 | if (err) return this.fallbackToHttp(done) | 189 | if (err) return this.fallbackToHttp(done) |
192 | 190 | ||
193 | if (!this.player.paused()) { | 191 | if (!this.player.paused()) return this.tryToPlay(done) |
194 | const playPromise = this.player.play() | ||
195 | if (playPromise !== undefined) return playPromise.then(done) | ||
196 | |||
197 | return done() | ||
198 | } | ||
199 | 192 | ||
200 | return done() | 193 | return done() |
201 | }) | 194 | }) |
@@ -264,6 +257,25 @@ class PeerTubePlugin extends Plugin { | |||
264 | this.trigger('autoResolutionUpdate') | 257 | this.trigger('autoResolutionUpdate') |
265 | } | 258 | } |
266 | 259 | ||
260 | private tryToPlay (done?: Function) { | ||
261 | if (!done) done = function () { /* empty */ } | ||
262 | |||
263 | const playPromise = this.player.play() | ||
264 | if (playPromise !== undefined) { | ||
265 | return playPromise.then(done) | ||
266 | .catch(err => { | ||
267 | console.error(err) | ||
268 | this.player.pause() | ||
269 | this.player.posterImage.show() | ||
270 | this.player.removeClass('vjs-has-autoplay') | ||
271 | |||
272 | return done() | ||
273 | }) | ||
274 | } | ||
275 | |||
276 | return done() | ||
277 | } | ||
278 | |||
267 | private seek (time: number) { | 279 | private seek (time: number) { |
268 | this.player.currentTime(time) | 280 | this.player.currentTime(time) |
269 | this.player.handleTechSeeked_() | 281 | this.player.handleTechSeeked_() |
@@ -317,7 +329,10 @@ class PeerTubePlugin extends Plugin { | |||
317 | if (this.autoplay === true) { | 329 | if (this.autoplay === true) { |
318 | this.player.posterImage.hide() | 330 | this.player.posterImage.hide() |
319 | 331 | ||
320 | this.updateVideoFile(undefined, 0, () => this.seek(this.startTime)) | 332 | this.updateVideoFile(undefined, 0, () => { |
333 | this.seek(this.startTime) | ||
334 | this.tryToPlay() | ||
335 | }) | ||
321 | } else { | 336 | } else { |
322 | // Proxy first play | 337 | // Proxy first play |
323 | const oldPlay = this.player.play.bind(this.player) | 338 | const oldPlay = this.player.play.bind(this.player) |
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts index a66caa30b..abdf333e1 100644 --- a/client/src/assets/player/peertube-videojs-typings.ts +++ b/client/src/assets/player/peertube-videojs-typings.ts | |||
@@ -22,6 +22,7 @@ type PeertubePluginOptions = { | |||
22 | videoViewUrl: string | 22 | videoViewUrl: string |
23 | videoDuration: number | 23 | videoDuration: number |
24 | startTime: number | 24 | startTime: number |
25 | autoplay: boolean | ||
25 | } | 26 | } |
26 | 27 | ||
27 | // videojs typings don't have some method we need | 28 | // videojs typings don't have some method we need |
@@ -215,7 +215,8 @@ async function startApplication () { | |||
215 | Redis.Instance.init() | 215 | Redis.Instance.init() |
216 | 216 | ||
217 | // Make server listening | 217 | // Make server listening |
218 | server.listen(port, hostname) | 218 | server.listen(port, hostname, () => { |
219 | logger.info('Server listening on %s:%d', hostname, port) | 219 | logger.info('Server listening on %s:%d', hostname, port) |
220 | logger.info('Web server: %s', CONFIG.WEBSERVER.URL) | 220 | logger.info('Web server: %s', CONFIG.WEBSERVER.URL) |
221 | }) | ||
221 | } | 222 | } |
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index bb0063473..e78361c9a 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -1,20 +1,22 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight } from '../../../../shared/models/users' | 2 | import { UserRight } from '../../../../shared/models/users' |
3 | import { sanitizeHost } from '../../../helpers/core-utils' | ||
4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
5 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
6 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 4 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' |
7 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' | 5 | import { sequelizeTypescript } from '../../../initializers' |
8 | import { REMOTE_SCHEME, sequelizeTypescript, SERVER_ACTOR_NAME } from '../../../initializers' | 6 | import { sendUndoFollow } from '../../../lib/activitypub/send' |
9 | import { getOrCreateActorAndServerAndModel } from '../../../lib/activitypub/actor' | ||
10 | import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send' | ||
11 | import { | 7 | import { |
12 | asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, removeFollowingValidator, setBodyHostsPort, setDefaultSort, | 8 | asyncMiddleware, |
13 | setDefaultPagination | 9 | authenticate, |
10 | ensureUserHasRight, | ||
11 | paginationValidator, | ||
12 | removeFollowingValidator, | ||
13 | setBodyHostsPort, | ||
14 | setDefaultPagination, | ||
15 | setDefaultSort | ||
14 | } from '../../../middlewares' | 16 | } from '../../../middlewares' |
15 | import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' | 17 | import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' |
16 | import { ActorModel } from '../../../models/activitypub/actor' | ||
17 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 18 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
19 | import { JobQueue } from '../../../lib/job-queue' | ||
18 | 20 | ||
19 | const serverFollowsRouter = express.Router() | 21 | const serverFollowsRouter = express.Router() |
20 | serverFollowsRouter.get('/following', | 22 | serverFollowsRouter.get('/following', |
@@ -30,7 +32,7 @@ serverFollowsRouter.post('/following', | |||
30 | ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), | 32 | ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), |
31 | followValidator, | 33 | followValidator, |
32 | setBodyHostsPort, | 34 | setBodyHostsPort, |
33 | asyncMiddleware(followRetry) | 35 | asyncMiddleware(followInstance) |
34 | ) | 36 | ) |
35 | 37 | ||
36 | serverFollowsRouter.delete('/following/:host', | 38 | serverFollowsRouter.delete('/following/:host', |
@@ -70,67 +72,17 @@ async function listFollowers (req: express.Request, res: express.Response, next: | |||
70 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 72 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
71 | } | 73 | } |
72 | 74 | ||
73 | async function followRetry (req: express.Request, res: express.Response, next: express.NextFunction) { | 75 | async function followInstance (req: express.Request, res: express.Response, next: express.NextFunction) { |
74 | const hosts = req.body.hosts as string[] | 76 | const hosts = req.body.hosts as string[] |
75 | const fromActor = await getServerActor() | ||
76 | |||
77 | const tasks: Promise<any>[] = [] | ||
78 | const actorName = SERVER_ACTOR_NAME | ||
79 | 77 | ||
80 | for (const host of hosts) { | 78 | for (const host of hosts) { |
81 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) | 79 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload: { host } }) |
82 | 80 | .catch(err => logger.error('Cannot create follow job for %s.', host, err)) | |
83 | // We process each host in a specific transaction | ||
84 | // First, we add the follow request in the database | ||
85 | // Then we send the follow request to other actor | ||
86 | const p = loadActorUrlOrGetFromWebfinger(actorName, sanitizedHost) | ||
87 | .then(actorUrl => getOrCreateActorAndServerAndModel(actorUrl)) | ||
88 | .then(targetActor => { | ||
89 | const options = { | ||
90 | arguments: [ fromActor, targetActor ], | ||
91 | errorMessage: 'Cannot follow with many retries.' | ||
92 | } | ||
93 | |||
94 | return retryTransactionWrapper(follow, options) | ||
95 | }) | ||
96 | .catch(err => logger.warn('Cannot follow server %s.', sanitizedHost, { err })) | ||
97 | |||
98 | tasks.push(p) | ||
99 | } | 81 | } |
100 | 82 | ||
101 | // Don't make the client wait the tasks | ||
102 | Promise.all(tasks) | ||
103 | .catch(err => logger.error('Error in follow.', { err })) | ||
104 | |||
105 | return res.status(204).end() | 83 | return res.status(204).end() |
106 | } | 84 | } |
107 | 85 | ||
108 | function follow (fromActor: ActorModel, targetActor: ActorModel) { | ||
109 | if (fromActor.id === targetActor.id) { | ||
110 | throw new Error('Follower is the same than target actor.') | ||
111 | } | ||
112 | |||
113 | return sequelizeTypescript.transaction(async t => { | ||
114 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | ||
115 | where: { | ||
116 | actorId: fromActor.id, | ||
117 | targetActorId: targetActor.id | ||
118 | }, | ||
119 | defaults: { | ||
120 | state: 'pending', | ||
121 | actorId: fromActor.id, | ||
122 | targetActorId: targetActor.id | ||
123 | }, | ||
124 | transaction: t | ||
125 | }) | ||
126 | actorFollow.ActorFollowing = targetActor | ||
127 | actorFollow.ActorFollower = fromActor | ||
128 | |||
129 | // Send a notification to remote server | ||
130 | await sendFollow(actorFollow) | ||
131 | }) | ||
132 | } | ||
133 | |||
134 | async function removeFollow (req: express.Request, res: express.Response, next: express.NextFunction) { | 86 | async function removeFollow (req: express.Request, res: express.Response, next: express.NextFunction) { |
135 | const follow: ActorFollowModel = res.locals.follow | 87 | const follow: ActorFollowModel = res.locals.follow |
136 | 88 | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 9fde989c5..5ee13389d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -65,6 +65,7 @@ const JOB_ATTEMPTS: { [ id in JobType ]: number } = { | |||
65 | 'activitypub-http-broadcast': 5, | 65 | 'activitypub-http-broadcast': 5, |
66 | 'activitypub-http-unicast': 5, | 66 | 'activitypub-http-unicast': 5, |
67 | 'activitypub-http-fetcher': 5, | 67 | 'activitypub-http-fetcher': 5, |
68 | 'activitypub-follow': 5, | ||
68 | 'video-file': 1, | 69 | 'video-file': 1, |
69 | 'email': 5 | 70 | 'email': 5 |
70 | } | 71 | } |
@@ -72,9 +73,11 @@ const JOB_CONCURRENCY: { [ id in JobType ]: number } = { | |||
72 | 'activitypub-http-broadcast': 1, | 73 | 'activitypub-http-broadcast': 1, |
73 | 'activitypub-http-unicast': 5, | 74 | 'activitypub-http-unicast': 5, |
74 | 'activitypub-http-fetcher': 1, | 75 | 'activitypub-http-fetcher': 1, |
76 | 'activitypub-follow': 3, | ||
75 | 'video-file': 1, | 77 | 'video-file': 1, |
76 | 'email': 5 | 78 | 'email': 5 |
77 | } | 79 | } |
80 | const BROADCAST_CONCURRENCY = 5 // How many requests in parallel we do in activitypub-http-broadcast job | ||
78 | // 2 days | 81 | // 2 days |
79 | const JOB_COMPLETED_LIFETIME = 60000 * 60 * 24 * 2 | 82 | const JOB_COMPLETED_LIFETIME = 60000 * 60 * 24 * 2 |
80 | 83 | ||
@@ -461,6 +464,7 @@ export { | |||
461 | LAST_MIGRATION_VERSION, | 464 | LAST_MIGRATION_VERSION, |
462 | OAUTH_LIFETIME, | 465 | OAUTH_LIFETIME, |
463 | OPENGRAPH_AND_OEMBED_COMMENT, | 466 | OPENGRAPH_AND_OEMBED_COMMENT, |
467 | BROADCAST_CONCURRENCY, | ||
464 | PAGINATION_COUNT_DEFAULT, | 468 | PAGINATION_COUNT_DEFAULT, |
465 | ACTOR_FOLLOW_SCORE, | 469 | ACTOR_FOLLOW_SCORE, |
466 | PREVIEWS_SIZE, | 470 | PREVIEWS_SIZE, |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts new file mode 100644 index 000000000..6764a4037 --- /dev/null +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -0,0 +1,68 @@ | |||
1 | import * as kue from 'kue' | ||
2 | import { logger } from '../../../helpers/logger' | ||
3 | import { getServerActor } from '../../../helpers/utils' | ||
4 | import { REMOTE_SCHEME, sequelizeTypescript, SERVER_ACTOR_NAME } from '../../../initializers' | ||
5 | import { sendFollow } from '../../activitypub/send' | ||
6 | import { sanitizeHost } from '../../../helpers/core-utils' | ||
7 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' | ||
8 | import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' | ||
9 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
10 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
11 | import { ActorModel } from '../../../models/activitypub/actor' | ||
12 | |||
13 | export type ActivitypubFollowPayload = { | ||
14 | host: string | ||
15 | } | ||
16 | |||
17 | async function processActivityPubFollow (job: kue.Job) { | ||
18 | const payload = job.data as ActivitypubFollowPayload | ||
19 | const host = payload.host | ||
20 | |||
21 | logger.info('Processing ActivityPub follow in job %d.', job.id) | ||
22 | |||
23 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) | ||
24 | |||
25 | const actorUrl = await loadActorUrlOrGetFromWebfinger(SERVER_ACTOR_NAME, sanitizedHost) | ||
26 | const targetActor = await getOrCreateActorAndServerAndModel(actorUrl) | ||
27 | |||
28 | const fromActor = await getServerActor() | ||
29 | const options = { | ||
30 | arguments: [ fromActor, targetActor ], | ||
31 | errorMessage: 'Cannot follow with many retries.' | ||
32 | } | ||
33 | |||
34 | return retryTransactionWrapper(follow, options) | ||
35 | } | ||
36 | // --------------------------------------------------------------------------- | ||
37 | |||
38 | export { | ||
39 | processActivityPubFollow | ||
40 | } | ||
41 | |||
42 | // --------------------------------------------------------------------------- | ||
43 | |||
44 | function follow (fromActor: ActorModel, targetActor: ActorModel) { | ||
45 | if (fromActor.id === targetActor.id) { | ||
46 | throw new Error('Follower is the same than target actor.') | ||
47 | } | ||
48 | |||
49 | return sequelizeTypescript.transaction(async t => { | ||
50 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | ||
51 | where: { | ||
52 | actorId: fromActor.id, | ||
53 | targetActorId: targetActor.id | ||
54 | }, | ||
55 | defaults: { | ||
56 | state: 'pending', | ||
57 | actorId: fromActor.id, | ||
58 | targetActorId: targetActor.id | ||
59 | }, | ||
60 | transaction: t | ||
61 | }) | ||
62 | actorFollow.ActorFollowing = targetActor | ||
63 | actorFollow.ActorFollower = fromActor | ||
64 | |||
65 | // Send a notification to remote server | ||
66 | await sendFollow(actorFollow) | ||
67 | }) | ||
68 | } | ||
diff --git a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts index 78878fc01..38b8393f4 100644 --- a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts +++ b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts | |||
@@ -1,8 +1,10 @@ | |||
1 | import * as kue from 'kue' | 1 | import * as kue from 'kue' |
2 | import * as Bluebird from 'bluebird' | ||
2 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
3 | import { doRequest } from '../../../helpers/requests' | 4 | import { doRequest } from '../../../helpers/requests' |
4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
5 | import { buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils' | 6 | import { buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils' |
7 | import { BROADCAST_CONCURRENCY } from '../../../initializers' | ||
6 | 8 | ||
7 | export type ActivitypubHttpBroadcastPayload = { | 9 | export type ActivitypubHttpBroadcastPayload = { |
8 | uris: string[] | 10 | uris: string[] |
@@ -28,16 +30,11 @@ async function processActivityPubHttpBroadcast (job: kue.Job) { | |||
28 | const badUrls: string[] = [] | 30 | const badUrls: string[] = [] |
29 | const goodUrls: string[] = [] | 31 | const goodUrls: string[] = [] |
30 | 32 | ||
31 | for (const uri of payload.uris) { | 33 | await Bluebird.map(payload.uris, uri => { |
32 | options.uri = uri | 34 | return doRequest(Object.assign({}, options, { uri })) |
33 | 35 | .then(() => goodUrls.push(uri)) | |
34 | try { | 36 | .catch(() => badUrls.push(uri)) |
35 | await doRequest(options) | 37 | }, { concurrency: BROADCAST_CONCURRENCY }) |
36 | goodUrls.push(uri) | ||
37 | } catch (err) { | ||
38 | badUrls.push(uri) | ||
39 | } | ||
40 | } | ||
41 | 38 | ||
42 | return ActorFollowModel.updateActorFollowsScore(goodUrls, badUrls, undefined) | 39 | return ActorFollowModel.updateActorFollowsScore(goodUrls, badUrls, undefined) |
43 | } | 40 | } |
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts index 1dc28755e..bf40a9206 100644 --- a/server/lib/job-queue/job-queue.ts +++ b/server/lib/job-queue/job-queue.ts | |||
@@ -8,11 +8,13 @@ import { ActivitypubHttpFetcherPayload, processActivityPubHttpFetcher } from './ | |||
8 | import { ActivitypubHttpUnicastPayload, processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast' | 8 | import { ActivitypubHttpUnicastPayload, processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast' |
9 | import { EmailPayload, processEmail } from './handlers/email' | 9 | import { EmailPayload, processEmail } from './handlers/email' |
10 | import { processVideoFile, VideoFilePayload } from './handlers/video-file' | 10 | import { processVideoFile, VideoFilePayload } from './handlers/video-file' |
11 | import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow' | ||
11 | 12 | ||
12 | type CreateJobArgument = | 13 | type CreateJobArgument = |
13 | { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } | | 14 | { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } | |
14 | { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } | | 15 | { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } | |
15 | { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } | | 16 | { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } | |
17 | { type: 'activitypub-follow', payload: ActivitypubFollowPayload } | | ||
16 | { type: 'video-file', payload: VideoFilePayload } | | 18 | { type: 'video-file', payload: VideoFilePayload } | |
17 | { type: 'email', payload: EmailPayload } | 19 | { type: 'email', payload: EmailPayload } |
18 | 20 | ||
@@ -20,6 +22,7 @@ const handlers: { [ id in JobType ]: (job: kue.Job) => Promise<any>} = { | |||
20 | 'activitypub-http-broadcast': processActivityPubHttpBroadcast, | 22 | 'activitypub-http-broadcast': processActivityPubHttpBroadcast, |
21 | 'activitypub-http-unicast': processActivityPubHttpUnicast, | 23 | 'activitypub-http-unicast': processActivityPubHttpUnicast, |
22 | 'activitypub-http-fetcher': processActivityPubHttpFetcher, | 24 | 'activitypub-http-fetcher': processActivityPubHttpFetcher, |
25 | 'activitypub-follow': processActivityPubFollow, | ||
23 | 'video-file': processVideoFile, | 26 | 'video-file': processVideoFile, |
24 | 'email': processEmail | 27 | 'email': processEmail |
25 | } | 28 | } |
@@ -50,7 +53,7 @@ class JobQueue { | |||
50 | } | 53 | } |
51 | }) | 54 | }) |
52 | 55 | ||
53 | this.jobQueue.setMaxListeners(15) | 56 | this.jobQueue.setMaxListeners(20) |
54 | 57 | ||
55 | this.jobQueue.on('error', err => { | 58 | this.jobQueue.on('error', err => { |
56 | logger.error('Error in job queue.', { err }) | 59 | logger.error('Error in job queue.', { err }) |
diff --git a/server/tests/utils/server/servers.ts b/server/tests/utils/server/servers.ts index 8373c73ab..1372c03c3 100644 --- a/server/tests/utils/server/servers.ts +++ b/server/tests/utils/server/servers.ts | |||
@@ -88,7 +88,7 @@ function runServer (serverNumber: number, configOverride?: Object) { | |||
88 | 88 | ||
89 | // These actions are async so we need to be sure that they have both been done | 89 | // These actions are async so we need to be sure that they have both been done |
90 | const serverRunString = { | 90 | const serverRunString = { |
91 | 'Server listening on port': false | 91 | 'Server listening': false |
92 | } | 92 | } |
93 | const key = 'Database peertube_test' + serverNumber + ' is ready' | 93 | const key = 'Database peertube_test' + serverNumber + ' is ready' |
94 | serverRunString[key] = false | 94 | serverRunString[key] = false |
diff --git a/shared/models/server/job.model.ts b/shared/models/server/job.model.ts index 5ebb75a5c..0fa36820e 100644 --- a/shared/models/server/job.model.ts +++ b/shared/models/server/job.model.ts | |||
@@ -3,6 +3,7 @@ export type JobState = 'active' | 'complete' | 'failed' | 'inactive' | 'delayed' | |||
3 | export type JobType = 'activitypub-http-unicast' | | 3 | export type JobType = 'activitypub-http-unicast' | |
4 | 'activitypub-http-broadcast' | | 4 | 'activitypub-http-broadcast' | |
5 | 'activitypub-http-fetcher' | | 5 | 'activitypub-http-fetcher' | |
6 | 'activitypub-follow' | | ||
6 | 'video-file' | | 7 | 'video-file' | |
7 | 'email' | 8 | 'email' |
8 | 9 | ||