diff options
Diffstat (limited to 'server/controllers/activitypub/client.ts')
-rw-r--r-- | server/controllers/activitypub/client.ts | 482 |
1 files changed, 0 insertions, 482 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts deleted file mode 100644 index be52f1662..000000000 --- a/server/controllers/activitypub/client.ts +++ /dev/null | |||
@@ -1,482 +0,0 @@ | |||
1 | import cors from 'cors' | ||
2 | import express from 'express' | ||
3 | import { activityPubCollectionPagination } from '@server/lib/activitypub/collection' | ||
4 | import { activityPubContextify } from '@server/lib/activitypub/context' | ||
5 | import { getServerActor } from '@server/models/application/application' | ||
6 | import { MAccountId, MActorId, MChannelId, MVideoId } from '@server/types/models' | ||
7 | import { VideoCommentObject } from '@shared/models' | ||
8 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' | ||
9 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | ||
10 | import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants' | ||
11 | import { audiencify, getAudience } from '../../lib/activitypub/audience' | ||
12 | import { buildAnnounceWithVideoAudience, buildLikeActivity } from '../../lib/activitypub/send' | ||
13 | import { buildCreateActivity } from '../../lib/activitypub/send/send-create' | ||
14 | import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' | ||
15 | import { | ||
16 | getLocalVideoCommentsActivityPubUrl, | ||
17 | getLocalVideoDislikesActivityPubUrl, | ||
18 | getLocalVideoLikesActivityPubUrl, | ||
19 | getLocalVideoSharesActivityPubUrl | ||
20 | } from '../../lib/activitypub/url' | ||
21 | import { | ||
22 | activityPubRateLimiter, | ||
23 | asyncMiddleware, | ||
24 | ensureIsLocalChannel, | ||
25 | executeIfActivityPub, | ||
26 | localAccountValidator, | ||
27 | videoChannelsNameWithHostValidator, | ||
28 | videosCustomGetValidator, | ||
29 | videosShareValidator | ||
30 | } from '../../middlewares' | ||
31 | import { cacheRoute } from '../../middlewares/cache/cache' | ||
32 | import { getAccountVideoRateValidatorFactory, getVideoLocalViewerValidator, videoCommentGetValidator } from '../../middlewares/validators' | ||
33 | import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' | ||
34 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' | ||
35 | import { AccountModel } from '../../models/account/account' | ||
36 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | ||
37 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
38 | import { VideoCommentModel } from '../../models/video/video-comment' | ||
39 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | ||
40 | import { VideoShareModel } from '../../models/video/video-share' | ||
41 | import { activityPubResponse } from './utils' | ||
42 | |||
43 | const activityPubClientRouter = express.Router() | ||
44 | activityPubClientRouter.use(cors()) | ||
45 | |||
46 | // Intercept ActivityPub client requests | ||
47 | |||
48 | activityPubClientRouter.get( | ||
49 | [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ], | ||
50 | executeIfActivityPub, | ||
51 | activityPubRateLimiter, | ||
52 | asyncMiddleware(localAccountValidator), | ||
53 | asyncMiddleware(accountController) | ||
54 | ) | ||
55 | activityPubClientRouter.get('/accounts?/:name/followers', | ||
56 | executeIfActivityPub, | ||
57 | activityPubRateLimiter, | ||
58 | asyncMiddleware(localAccountValidator), | ||
59 | asyncMiddleware(accountFollowersController) | ||
60 | ) | ||
61 | activityPubClientRouter.get('/accounts?/:name/following', | ||
62 | executeIfActivityPub, | ||
63 | activityPubRateLimiter, | ||
64 | asyncMiddleware(localAccountValidator), | ||
65 | asyncMiddleware(accountFollowingController) | ||
66 | ) | ||
67 | activityPubClientRouter.get('/accounts?/:name/playlists', | ||
68 | executeIfActivityPub, | ||
69 | activityPubRateLimiter, | ||
70 | asyncMiddleware(localAccountValidator), | ||
71 | asyncMiddleware(accountPlaylistsController) | ||
72 | ) | ||
73 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', | ||
74 | executeIfActivityPub, | ||
75 | activityPubRateLimiter, | ||
76 | cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), | ||
77 | asyncMiddleware(getAccountVideoRateValidatorFactory('like')), | ||
78 | asyncMiddleware(getAccountVideoRateFactory('like')) | ||
79 | ) | ||
80 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | ||
81 | executeIfActivityPub, | ||
82 | activityPubRateLimiter, | ||
83 | cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), | ||
84 | asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), | ||
85 | asyncMiddleware(getAccountVideoRateFactory('dislike')) | ||
86 | ) | ||
87 | |||
88 | activityPubClientRouter.get( | ||
89 | [ '/videos/watch/:id', '/w/:id' ], | ||
90 | executeIfActivityPub, | ||
91 | activityPubRateLimiter, | ||
92 | cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), | ||
93 | asyncMiddleware(videosCustomGetValidator('all')), | ||
94 | asyncMiddleware(videoController) | ||
95 | ) | ||
96 | activityPubClientRouter.get('/videos/watch/:id/activity', | ||
97 | executeIfActivityPub, | ||
98 | activityPubRateLimiter, | ||
99 | asyncMiddleware(videosCustomGetValidator('all')), | ||
100 | asyncMiddleware(videoController) | ||
101 | ) | ||
102 | activityPubClientRouter.get('/videos/watch/:id/announces', | ||
103 | executeIfActivityPub, | ||
104 | activityPubRateLimiter, | ||
105 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), | ||
106 | asyncMiddleware(videoAnnouncesController) | ||
107 | ) | ||
108 | activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', | ||
109 | executeIfActivityPub, | ||
110 | activityPubRateLimiter, | ||
111 | asyncMiddleware(videosShareValidator), | ||
112 | asyncMiddleware(videoAnnounceController) | ||
113 | ) | ||
114 | activityPubClientRouter.get('/videos/watch/:id/likes', | ||
115 | executeIfActivityPub, | ||
116 | activityPubRateLimiter, | ||
117 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), | ||
118 | asyncMiddleware(videoLikesController) | ||
119 | ) | ||
120 | activityPubClientRouter.get('/videos/watch/:id/dislikes', | ||
121 | executeIfActivityPub, | ||
122 | activityPubRateLimiter, | ||
123 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), | ||
124 | asyncMiddleware(videoDislikesController) | ||
125 | ) | ||
126 | activityPubClientRouter.get('/videos/watch/:id/comments', | ||
127 | executeIfActivityPub, | ||
128 | activityPubRateLimiter, | ||
129 | asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')), | ||
130 | asyncMiddleware(videoCommentsController) | ||
131 | ) | ||
132 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', | ||
133 | executeIfActivityPub, | ||
134 | activityPubRateLimiter, | ||
135 | asyncMiddleware(videoCommentGetValidator), | ||
136 | asyncMiddleware(videoCommentController) | ||
137 | ) | ||
138 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity', | ||
139 | executeIfActivityPub, | ||
140 | activityPubRateLimiter, | ||
141 | asyncMiddleware(videoCommentGetValidator), | ||
142 | asyncMiddleware(videoCommentController) | ||
143 | ) | ||
144 | |||
145 | activityPubClientRouter.get( | ||
146 | [ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ], | ||
147 | executeIfActivityPub, | ||
148 | activityPubRateLimiter, | ||
149 | asyncMiddleware(videoChannelsNameWithHostValidator), | ||
150 | ensureIsLocalChannel, | ||
151 | asyncMiddleware(videoChannelController) | ||
152 | ) | ||
153 | activityPubClientRouter.get('/video-channels/:nameWithHost/followers', | ||
154 | executeIfActivityPub, | ||
155 | activityPubRateLimiter, | ||
156 | asyncMiddleware(videoChannelsNameWithHostValidator), | ||
157 | ensureIsLocalChannel, | ||
158 | asyncMiddleware(videoChannelFollowersController) | ||
159 | ) | ||
160 | activityPubClientRouter.get('/video-channels/:nameWithHost/following', | ||
161 | executeIfActivityPub, | ||
162 | activityPubRateLimiter, | ||
163 | asyncMiddleware(videoChannelsNameWithHostValidator), | ||
164 | ensureIsLocalChannel, | ||
165 | asyncMiddleware(videoChannelFollowingController) | ||
166 | ) | ||
167 | activityPubClientRouter.get('/video-channels/:nameWithHost/playlists', | ||
168 | executeIfActivityPub, | ||
169 | activityPubRateLimiter, | ||
170 | asyncMiddleware(videoChannelsNameWithHostValidator), | ||
171 | ensureIsLocalChannel, | ||
172 | asyncMiddleware(videoChannelPlaylistsController) | ||
173 | ) | ||
174 | |||
175 | activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', | ||
176 | executeIfActivityPub, | ||
177 | activityPubRateLimiter, | ||
178 | asyncMiddleware(videoFileRedundancyGetValidator), | ||
179 | asyncMiddleware(videoRedundancyController) | ||
180 | ) | ||
181 | activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistType/:videoId', | ||
182 | executeIfActivityPub, | ||
183 | activityPubRateLimiter, | ||
184 | asyncMiddleware(videoPlaylistRedundancyGetValidator), | ||
185 | asyncMiddleware(videoRedundancyController) | ||
186 | ) | ||
187 | |||
188 | activityPubClientRouter.get( | ||
189 | [ '/video-playlists/:playlistId', '/videos/watch/playlist/:playlistId', '/w/p/:playlistId' ], | ||
190 | executeIfActivityPub, | ||
191 | activityPubRateLimiter, | ||
192 | asyncMiddleware(videoPlaylistsGetValidator('all')), | ||
193 | asyncMiddleware(videoPlaylistController) | ||
194 | ) | ||
195 | activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', | ||
196 | executeIfActivityPub, | ||
197 | activityPubRateLimiter, | ||
198 | asyncMiddleware(videoPlaylistElementAPGetValidator), | ||
199 | asyncMiddleware(videoPlaylistElementController) | ||
200 | ) | ||
201 | |||
202 | activityPubClientRouter.get('/videos/local-viewer/:localViewerId', | ||
203 | executeIfActivityPub, | ||
204 | activityPubRateLimiter, | ||
205 | asyncMiddleware(getVideoLocalViewerValidator), | ||
206 | asyncMiddleware(getVideoLocalViewerController) | ||
207 | ) | ||
208 | |||
209 | // --------------------------------------------------------------------------- | ||
210 | |||
211 | export { | ||
212 | activityPubClientRouter | ||
213 | } | ||
214 | |||
215 | // --------------------------------------------------------------------------- | ||
216 | |||
217 | async function accountController (req: express.Request, res: express.Response) { | ||
218 | const account = res.locals.account | ||
219 | |||
220 | return activityPubResponse(activityPubContextify(await account.toActivityPubObject(), 'Actor'), res) | ||
221 | } | ||
222 | |||
223 | async function accountFollowersController (req: express.Request, res: express.Response) { | ||
224 | const account = res.locals.account | ||
225 | const activityPubResult = await actorFollowers(req, account.Actor) | ||
226 | |||
227 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
228 | } | ||
229 | |||
230 | async function accountFollowingController (req: express.Request, res: express.Response) { | ||
231 | const account = res.locals.account | ||
232 | const activityPubResult = await actorFollowing(req, account.Actor) | ||
233 | |||
234 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
235 | } | ||
236 | |||
237 | async function accountPlaylistsController (req: express.Request, res: express.Response) { | ||
238 | const account = res.locals.account | ||
239 | const activityPubResult = await actorPlaylists(req, { account }) | ||
240 | |||
241 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
242 | } | ||
243 | |||
244 | async function videoChannelPlaylistsController (req: express.Request, res: express.Response) { | ||
245 | const channel = res.locals.videoChannel | ||
246 | const activityPubResult = await actorPlaylists(req, { channel }) | ||
247 | |||
248 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
249 | } | ||
250 | |||
251 | function getAccountVideoRateFactory (rateType: VideoRateType) { | ||
252 | return (req: express.Request, res: express.Response) => { | ||
253 | const accountVideoRate = res.locals.accountVideoRate | ||
254 | |||
255 | const byActor = accountVideoRate.Account.Actor | ||
256 | const APObject = rateType === 'like' | ||
257 | ? buildLikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) | ||
258 | : buildDislikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video) | ||
259 | |||
260 | return activityPubResponse(activityPubContextify(APObject, 'Rate'), res) | ||
261 | } | ||
262 | } | ||
263 | |||
264 | async function videoController (req: express.Request, res: express.Response) { | ||
265 | const video = res.locals.videoAll | ||
266 | |||
267 | if (redirectIfNotOwned(video.url, res)) return | ||
268 | |||
269 | // We need captions to render AP object | ||
270 | const videoAP = await video.lightAPToFullAP(undefined) | ||
271 | |||
272 | const audience = getAudience(videoAP.VideoChannel.Account.Actor, videoAP.privacy === VideoPrivacy.PUBLIC) | ||
273 | const videoObject = audiencify(await videoAP.toActivityPubObject(), audience) | ||
274 | |||
275 | if (req.path.endsWith('/activity')) { | ||
276 | const data = buildCreateActivity(videoAP.url, video.VideoChannel.Account.Actor, videoObject, audience) | ||
277 | return activityPubResponse(activityPubContextify(data, 'Video'), res) | ||
278 | } | ||
279 | |||
280 | return activityPubResponse(activityPubContextify(videoObject, 'Video'), res) | ||
281 | } | ||
282 | |||
283 | async function videoAnnounceController (req: express.Request, res: express.Response) { | ||
284 | const share = res.locals.videoShare | ||
285 | |||
286 | if (redirectIfNotOwned(share.url, res)) return | ||
287 | |||
288 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined) | ||
289 | |||
290 | return activityPubResponse(activityPubContextify(activity, 'Announce'), res) | ||
291 | } | ||
292 | |||
293 | async function videoAnnouncesController (req: express.Request, res: express.Response) { | ||
294 | const video = res.locals.onlyImmutableVideo | ||
295 | |||
296 | if (redirectIfNotOwned(video.url, res)) return | ||
297 | |||
298 | const handler = async (start: number, count: number) => { | ||
299 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) | ||
300 | return { | ||
301 | total: result.total, | ||
302 | data: result.data.map(r => r.url) | ||
303 | } | ||
304 | } | ||
305 | const json = await activityPubCollectionPagination(getLocalVideoSharesActivityPubUrl(video), handler, req.query.page) | ||
306 | |||
307 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) | ||
308 | } | ||
309 | |||
310 | async function videoLikesController (req: express.Request, res: express.Response) { | ||
311 | const video = res.locals.onlyImmutableVideo | ||
312 | |||
313 | if (redirectIfNotOwned(video.url, res)) return | ||
314 | |||
315 | const json = await videoRates(req, 'like', video, getLocalVideoLikesActivityPubUrl(video)) | ||
316 | |||
317 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) | ||
318 | } | ||
319 | |||
320 | async function videoDislikesController (req: express.Request, res: express.Response) { | ||
321 | const video = res.locals.onlyImmutableVideo | ||
322 | |||
323 | if (redirectIfNotOwned(video.url, res)) return | ||
324 | |||
325 | const json = await videoRates(req, 'dislike', video, getLocalVideoDislikesActivityPubUrl(video)) | ||
326 | |||
327 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) | ||
328 | } | ||
329 | |||
330 | async function videoCommentsController (req: express.Request, res: express.Response) { | ||
331 | const video = res.locals.onlyImmutableVideo | ||
332 | |||
333 | if (redirectIfNotOwned(video.url, res)) return | ||
334 | |||
335 | const handler = async (start: number, count: number) => { | ||
336 | const result = await VideoCommentModel.listAndCountByVideoForAP({ video, start, count }) | ||
337 | |||
338 | return { | ||
339 | total: result.total, | ||
340 | data: result.data.map(r => r.url) | ||
341 | } | ||
342 | } | ||
343 | const json = await activityPubCollectionPagination(getLocalVideoCommentsActivityPubUrl(video), handler, req.query.page) | ||
344 | |||
345 | return activityPubResponse(activityPubContextify(json, 'Collection'), res) | ||
346 | } | ||
347 | |||
348 | async function videoChannelController (req: express.Request, res: express.Response) { | ||
349 | const videoChannel = res.locals.videoChannel | ||
350 | |||
351 | return activityPubResponse(activityPubContextify(await videoChannel.toActivityPubObject(), 'Actor'), res) | ||
352 | } | ||
353 | |||
354 | async function videoChannelFollowersController (req: express.Request, res: express.Response) { | ||
355 | const videoChannel = res.locals.videoChannel | ||
356 | const activityPubResult = await actorFollowers(req, videoChannel.Actor) | ||
357 | |||
358 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
359 | } | ||
360 | |||
361 | async function videoChannelFollowingController (req: express.Request, res: express.Response) { | ||
362 | const videoChannel = res.locals.videoChannel | ||
363 | const activityPubResult = await actorFollowing(req, videoChannel.Actor) | ||
364 | |||
365 | return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res) | ||
366 | } | ||
367 | |||
368 | async function videoCommentController (req: express.Request, res: express.Response) { | ||
369 | const videoComment = res.locals.videoCommentFull | ||
370 | |||
371 | if (redirectIfNotOwned(videoComment.url, res)) return | ||
372 | |||
373 | const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, undefined) | ||
374 | const isPublic = true // Comments are always public | ||
375 | let videoCommentObject = videoComment.toActivityPubObject(threadParentComments) | ||
376 | |||
377 | if (videoComment.Account) { | ||
378 | const audience = getAudience(videoComment.Account.Actor, isPublic) | ||
379 | videoCommentObject = audiencify(videoCommentObject, audience) | ||
380 | |||
381 | if (req.path.endsWith('/activity')) { | ||
382 | const data = buildCreateActivity(videoComment.url, videoComment.Account.Actor, videoCommentObject as VideoCommentObject, audience) | ||
383 | return activityPubResponse(activityPubContextify(data, 'Comment'), res) | ||
384 | } | ||
385 | } | ||
386 | |||
387 | return activityPubResponse(activityPubContextify(videoCommentObject, 'Comment'), res) | ||
388 | } | ||
389 | |||
390 | async function videoRedundancyController (req: express.Request, res: express.Response) { | ||
391 | const videoRedundancy = res.locals.videoRedundancy | ||
392 | |||
393 | if (redirectIfNotOwned(videoRedundancy.url, res)) return | ||
394 | |||
395 | const serverActor = await getServerActor() | ||
396 | |||
397 | const audience = getAudience(serverActor) | ||
398 | const object = audiencify(videoRedundancy.toActivityPubObject(), audience) | ||
399 | |||
400 | if (req.path.endsWith('/activity')) { | ||
401 | const data = buildCreateActivity(videoRedundancy.url, serverActor, object, audience) | ||
402 | return activityPubResponse(activityPubContextify(data, 'CacheFile'), res) | ||
403 | } | ||
404 | |||
405 | return activityPubResponse(activityPubContextify(object, 'CacheFile'), res) | ||
406 | } | ||
407 | |||
408 | async function videoPlaylistController (req: express.Request, res: express.Response) { | ||
409 | const playlist = res.locals.videoPlaylistFull | ||
410 | |||
411 | if (redirectIfNotOwned(playlist.url, res)) return | ||
412 | |||
413 | // We need more attributes | ||
414 | playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId) | ||
415 | |||
416 | const json = await playlist.toActivityPubObject(req.query.page, null) | ||
417 | const audience = getAudience(playlist.OwnerAccount.Actor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC) | ||
418 | const object = audiencify(json, audience) | ||
419 | |||
420 | return activityPubResponse(activityPubContextify(object, 'Playlist'), res) | ||
421 | } | ||
422 | |||
423 | function videoPlaylistElementController (req: express.Request, res: express.Response) { | ||
424 | const videoPlaylistElement = res.locals.videoPlaylistElementAP | ||
425 | |||
426 | if (redirectIfNotOwned(videoPlaylistElement.url, res)) return | ||
427 | |||
428 | const json = videoPlaylistElement.toActivityPubObject() | ||
429 | return activityPubResponse(activityPubContextify(json, 'Playlist'), res) | ||
430 | } | ||
431 | |||
432 | function getVideoLocalViewerController (req: express.Request, res: express.Response) { | ||
433 | const localViewer = res.locals.localViewerFull | ||
434 | |||
435 | return activityPubResponse(activityPubContextify(localViewer.toActivityPubObject(), 'WatchAction'), res) | ||
436 | } | ||
437 | |||
438 | // --------------------------------------------------------------------------- | ||
439 | |||
440 | function actorFollowing (req: express.Request, actor: MActorId) { | ||
441 | const handler = (start: number, count: number) => { | ||
442 | return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) | ||
443 | } | ||
444 | |||
445 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | ||
446 | } | ||
447 | |||
448 | function actorFollowers (req: express.Request, actor: MActorId) { | ||
449 | const handler = (start: number, count: number) => { | ||
450 | return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count) | ||
451 | } | ||
452 | |||
453 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | ||
454 | } | ||
455 | |||
456 | function actorPlaylists (req: express.Request, options: { account: MAccountId } | { channel: MChannelId }) { | ||
457 | const handler = (start: number, count: number) => { | ||
458 | return VideoPlaylistModel.listPublicUrlsOfForAP(options, start, count) | ||
459 | } | ||
460 | |||
461 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | ||
462 | } | ||
463 | |||
464 | function videoRates (req: express.Request, rateType: VideoRateType, video: MVideoId, url: string) { | ||
465 | const handler = async (start: number, count: number) => { | ||
466 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) | ||
467 | return { | ||
468 | total: result.total, | ||
469 | data: result.data.map(r => r.url) | ||
470 | } | ||
471 | } | ||
472 | return activityPubCollectionPagination(url, handler, req.query.page) | ||
473 | } | ||
474 | |||
475 | function redirectIfNotOwned (url: string, res: express.Response) { | ||
476 | if (url.startsWith(WEBSERVER.URL) === false) { | ||
477 | res.redirect(url) | ||
478 | return true | ||
479 | } | ||
480 | |||
481 | return false | ||
482 | } | ||