]>
Commit | Line | Data |
---|---|---|
e4f97bab C |
1 | // Intercept ActivityPub client requests |
2 | import * as express from 'express' | |
8fffe21a | 3 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' |
d6e99e53 | 4 | import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' |
8fffe21a | 5 | import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers' |
5c6d985f | 6 | import { buildAnnounceWithVideoAudience, buildDislikeActivity, buildLikeActivity } from '../../lib/activitypub/send' |
e251f170 | 7 | import { audiencify, getAudience } from '../../lib/activitypub/audience' |
c48e82b5 | 8 | import { buildCreateActivity } from '../../lib/activitypub/send/send-create' |
96f29c0f C |
9 | import { |
10 | asyncMiddleware, | |
5c6d985f | 11 | videosShareValidator, |
96f29c0f C |
12 | executeIfActivityPub, |
13 | localAccountValidator, | |
14 | localVideoChannelValidator, | |
15 | videosCustomGetValidator | |
16 | } from '../../middlewares' | |
5c6d985f C |
17 | import { |
18 | getAccountVideoRateValidator, | |
19 | videoCommentGetValidator, | |
20 | videosGetValidator | |
21 | } from '../../middlewares/validators' | |
3fd3ab2d | 22 | import { AccountModel } from '../../models/account/account' |
7006bc63 | 23 | import { ActorModel } from '../../models/activitypub/actor' |
50d6de9c | 24 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' |
3fd3ab2d C |
25 | import { VideoModel } from '../../models/video/video' |
26 | import { VideoChannelModel } from '../../models/video/video-channel' | |
da854ddd | 27 | import { VideoCommentModel } from '../../models/video/video-comment' |
3fd3ab2d | 28 | import { VideoShareModel } from '../../models/video/video-share' |
98d3324d | 29 | import { cacheRoute } from '../../middlewares/cache' |
8fffe21a C |
30 | import { activityPubResponse } from './utils' |
31 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | |
32 | import { | |
5c6d985f | 33 | getRateUrl, |
8fffe21a C |
34 | getVideoCommentsActivityPubUrl, |
35 | getVideoDislikesActivityPubUrl, | |
36 | getVideoLikesActivityPubUrl, | |
37 | getVideoSharesActivityPubUrl | |
38 | } from '../../lib/activitypub' | |
40e87e9e | 39 | import { VideoCaptionModel } from '../../models/video/video-caption' |
c48e82b5 C |
40 | import { videoRedundancyGetValidator } from '../../middlewares/validators/redundancy' |
41 | import { getServerActor } from '../../helpers/utils' | |
8d1fa36a | 42 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
e4f97bab C |
43 | |
44 | const activityPubClientRouter = express.Router() | |
45 | ||
108af661 | 46 | activityPubClientRouter.get('/accounts?/:name', |
a2431b7d | 47 | executeIfActivityPub(asyncMiddleware(localAccountValidator)), |
4e50b6a1 | 48 | executeIfActivityPub(accountController) |
e4f97bab | 49 | ) |
7006bc63 | 50 | activityPubClientRouter.get('/accounts?/:name/followers', |
a2431b7d | 51 | executeIfActivityPub(asyncMiddleware(localAccountValidator)), |
e4f97bab C |
52 | executeIfActivityPub(asyncMiddleware(accountFollowersController)) |
53 | ) | |
7006bc63 | 54 | activityPubClientRouter.get('/accounts?/:name/following', |
a2431b7d | 55 | executeIfActivityPub(asyncMiddleware(localAccountValidator)), |
e4f97bab C |
56 | executeIfActivityPub(asyncMiddleware(accountFollowingController)) |
57 | ) | |
5c6d985f C |
58 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', |
59 | executeIfActivityPub(asyncMiddleware(getAccountVideoRateValidator('like'))), | |
60 | executeIfActivityPub(getAccountVideoRate('like')) | |
61 | ) | |
62 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | |
63 | executeIfActivityPub(asyncMiddleware(getAccountVideoRateValidator('dislike'))), | |
64 | executeIfActivityPub(getAccountVideoRate('dislike')) | |
65 | ) | |
e4f97bab | 66 | |
20494f12 | 67 | activityPubClientRouter.get('/videos/watch/:id', |
98d3324d | 68 | executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))), |
a2431b7d | 69 | executeIfActivityPub(asyncMiddleware(videosGetValidator)), |
da854ddd | 70 | executeIfActivityPub(asyncMiddleware(videoController)) |
4e50b6a1 | 71 | ) |
296c0905 C |
72 | activityPubClientRouter.get('/videos/watch/:id/activity', |
73 | executeIfActivityPub(asyncMiddleware(videosGetValidator)), | |
74 | executeIfActivityPub(asyncMiddleware(videoController)) | |
75 | ) | |
46531a0a | 76 | activityPubClientRouter.get('/videos/watch/:id/announces', |
96f29c0f | 77 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))), |
46531a0a C |
78 | executeIfActivityPub(asyncMiddleware(videoAnnouncesController)) |
79 | ) | |
5c6d985f | 80 | activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', |
4e50b6a1 C |
81 | executeIfActivityPub(asyncMiddleware(videosShareValidator)), |
82 | executeIfActivityPub(asyncMiddleware(videoAnnounceController)) | |
20494f12 | 83 | ) |
46531a0a | 84 | activityPubClientRouter.get('/videos/watch/:id/likes', |
96f29c0f | 85 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))), |
46531a0a C |
86 | executeIfActivityPub(asyncMiddleware(videoLikesController)) |
87 | ) | |
88 | activityPubClientRouter.get('/videos/watch/:id/dislikes', | |
96f29c0f | 89 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))), |
46531a0a C |
90 | executeIfActivityPub(asyncMiddleware(videoDislikesController)) |
91 | ) | |
92 | activityPubClientRouter.get('/videos/watch/:id/comments', | |
96f29c0f | 93 | executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))), |
46531a0a C |
94 | executeIfActivityPub(asyncMiddleware(videoCommentsController)) |
95 | ) | |
da854ddd C |
96 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId', |
97 | executeIfActivityPub(asyncMiddleware(videoCommentGetValidator)), | |
98 | executeIfActivityPub(asyncMiddleware(videoCommentController)) | |
99 | ) | |
296c0905 C |
100 | activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity', |
101 | executeIfActivityPub(asyncMiddleware(videoCommentGetValidator)), | |
102 | executeIfActivityPub(asyncMiddleware(videoCommentController)) | |
103 | ) | |
da854ddd | 104 | |
8a19bee1 C |
105 | activityPubClientRouter.get('/video-channels/:name', |
106 | executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), | |
20494f12 C |
107 | executeIfActivityPub(asyncMiddleware(videoChannelController)) |
108 | ) | |
8a19bee1 C |
109 | activityPubClientRouter.get('/video-channels/:name/followers', |
110 | executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), | |
7006bc63 C |
111 | executeIfActivityPub(asyncMiddleware(videoChannelFollowersController)) |
112 | ) | |
8a19bee1 C |
113 | activityPubClientRouter.get('/video-channels/:name/following', |
114 | executeIfActivityPub(asyncMiddleware(localVideoChannelValidator)), | |
7006bc63 C |
115 | executeIfActivityPub(asyncMiddleware(videoChannelFollowingController)) |
116 | ) | |
20494f12 | 117 | |
c48e82b5 C |
118 | activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', |
119 | executeIfActivityPub(asyncMiddleware(videoRedundancyGetValidator)), | |
120 | executeIfActivityPub(asyncMiddleware(videoRedundancyController)) | |
121 | ) | |
122 | ||
e4f97bab C |
123 | // --------------------------------------------------------------------------- |
124 | ||
125 | export { | |
126 | activityPubClientRouter | |
127 | } | |
128 | ||
129 | // --------------------------------------------------------------------------- | |
130 | ||
4e50b6a1 | 131 | function accountController (req: express.Request, res: express.Response, next: express.NextFunction) { |
3fd3ab2d | 132 | const account: AccountModel = res.locals.account |
e4f97bab | 133 | |
4b8f09fa | 134 | return activityPubResponse(activityPubContextify(account.toActivityPubObject()), res) |
e4f97bab C |
135 | } |
136 | ||
137 | async function accountFollowersController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
3fd3ab2d | 138 | const account: AccountModel = res.locals.account |
7006bc63 | 139 | const activityPubResult = await actorFollowers(req, account.Actor) |
e4f97bab | 140 | |
4b8f09fa | 141 | return activityPubResponse(activityPubContextify(activityPubResult), res) |
e4f97bab C |
142 | } |
143 | ||
144 | async function accountFollowingController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
3fd3ab2d | 145 | const account: AccountModel = res.locals.account |
7006bc63 | 146 | const activityPubResult = await actorFollowing(req, account.Actor) |
e4f97bab | 147 | |
4b8f09fa | 148 | return activityPubResponse(activityPubContextify(activityPubResult), res) |
e4f97bab | 149 | } |
20494f12 | 150 | |
5c6d985f C |
151 | function getAccountVideoRate (rateType: VideoRateType) { |
152 | return (req: express.Request, res: express.Response) => { | |
153 | const accountVideoRate: AccountVideoRateModel = res.locals.accountVideoRate | |
154 | ||
155 | const byActor = accountVideoRate.Account.Actor | |
156 | const url = getRateUrl(rateType, byActor, accountVideoRate.Video) | |
157 | const APObject = rateType === 'like' | |
158 | ? buildLikeActivity(url, byActor, accountVideoRate.Video) | |
159 | : buildCreateActivity(url, byActor, buildDislikeActivity(url, byActor, accountVideoRate.Video)) | |
160 | ||
161 | return activityPubResponse(activityPubContextify(APObject), res) | |
162 | } | |
163 | } | |
164 | ||
da854ddd | 165 | async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { |
3fd3ab2d | 166 | const video: VideoModel = res.locals.video |
20494f12 | 167 | |
8d1fa36a C |
168 | if (video.isOwned() === false) return res.redirect(video.url) |
169 | ||
40e87e9e C |
170 | // We need captions to render AP object |
171 | video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) | |
172 | ||
2186386c | 173 | const audience = getAudience(video.VideoChannel.Account.Actor, video.privacy === VideoPrivacy.PUBLIC) |
8fffe21a | 174 | const videoObject = audiencify(video.toActivityPubObject(), audience) |
2ccaeeb3 | 175 | |
296c0905 | 176 | if (req.path.endsWith('/activity')) { |
c48e82b5 | 177 | const data = buildCreateActivity(video.url, video.VideoChannel.Account.Actor, videoObject, audience) |
4b8f09fa | 178 | return activityPubResponse(activityPubContextify(data), res) |
296c0905 C |
179 | } |
180 | ||
4b8f09fa | 181 | return activityPubResponse(activityPubContextify(videoObject), res) |
20494f12 C |
182 | } |
183 | ||
4e50b6a1 | 184 | async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { |
3fd3ab2d | 185 | const share = res.locals.videoShare as VideoShareModel |
8d1fa36a C |
186 | |
187 | if (share.Actor.isOwned() === false) return res.redirect(share.url) | |
188 | ||
c48e82b5 | 189 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) |
4e50b6a1 | 190 | |
c48e82b5 | 191 | return activityPubResponse(activityPubContextify(activity), res) |
4e50b6a1 C |
192 | } |
193 | ||
46531a0a C |
194 | async function videoAnnouncesController (req: express.Request, res: express.Response, next: express.NextFunction) { |
195 | const video: VideoModel = res.locals.video | |
196 | ||
8fffe21a C |
197 | const handler = async (start: number, count: number) => { |
198 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) | |
199 | return { | |
200 | total: result.count, | |
201 | data: result.rows.map(r => r.url) | |
202 | } | |
203 | } | |
204 | const json = await activityPubCollectionPagination(getVideoSharesActivityPubUrl(video), handler, req.query.page) | |
46531a0a | 205 | |
8fffe21a | 206 | return activityPubResponse(activityPubContextify(json), res) |
46531a0a C |
207 | } |
208 | ||
209 | async function videoLikesController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
210 | const video: VideoModel = res.locals.video | |
8fffe21a | 211 | const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) |
46531a0a | 212 | |
8fffe21a | 213 | return activityPubResponse(activityPubContextify(json), res) |
46531a0a C |
214 | } |
215 | ||
216 | async function videoDislikesController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
217 | const video: VideoModel = res.locals.video | |
8fffe21a | 218 | const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) |
46531a0a | 219 | |
8fffe21a | 220 | return activityPubResponse(activityPubContextify(json), res) |
46531a0a C |
221 | } |
222 | ||
223 | async function videoCommentsController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
224 | const video: VideoModel = res.locals.video | |
225 | ||
8fffe21a C |
226 | const handler = async (start: number, count: number) => { |
227 | const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) | |
228 | return { | |
229 | total: result.count, | |
230 | data: result.rows.map(r => r.url) | |
231 | } | |
232 | } | |
233 | const json = await activityPubCollectionPagination(getVideoCommentsActivityPubUrl(video), handler, req.query.page) | |
46531a0a | 234 | |
8fffe21a | 235 | return activityPubResponse(activityPubContextify(json), res) |
46531a0a C |
236 | } |
237 | ||
20494f12 | 238 | async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) { |
3fd3ab2d | 239 | const videoChannel: VideoChannelModel = res.locals.videoChannel |
20494f12 | 240 | |
4b8f09fa | 241 | return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) |
20494f12 | 242 | } |
da854ddd | 243 | |
7006bc63 C |
244 | async function videoChannelFollowersController (req: express.Request, res: express.Response, next: express.NextFunction) { |
245 | const videoChannel: VideoChannelModel = res.locals.videoChannel | |
246 | const activityPubResult = await actorFollowers(req, videoChannel.Actor) | |
247 | ||
4b8f09fa | 248 | return activityPubResponse(activityPubContextify(activityPubResult), res) |
7006bc63 C |
249 | } |
250 | ||
251 | async function videoChannelFollowingController (req: express.Request, res: express.Response, next: express.NextFunction) { | |
252 | const videoChannel: VideoChannelModel = res.locals.videoChannel | |
253 | const activityPubResult = await actorFollowing(req, videoChannel.Actor) | |
254 | ||
4b8f09fa | 255 | return activityPubResponse(activityPubContextify(activityPubResult), res) |
7006bc63 C |
256 | } |
257 | ||
da854ddd C |
258 | async function videoCommentController (req: express.Request, res: express.Response, next: express.NextFunction) { |
259 | const videoComment: VideoCommentModel = res.locals.videoComment | |
260 | ||
8d1fa36a C |
261 | if (videoComment.isOwned() === false) return res.redirect(videoComment.url) |
262 | ||
d7e70384 | 263 | const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, undefined) |
d6e99e53 | 264 | const isPublic = true // Comments are always public |
2186386c | 265 | const audience = getAudience(videoComment.Account.Actor, isPublic) |
d6e99e53 C |
266 | |
267 | const videoCommentObject = audiencify(videoComment.toActivityPubObject(threadParentComments), audience) | |
268 | ||
296c0905 | 269 | if (req.path.endsWith('/activity')) { |
c48e82b5 | 270 | const data = buildCreateActivity(videoComment.url, videoComment.Account.Actor, videoCommentObject, audience) |
4b8f09fa | 271 | return activityPubResponse(activityPubContextify(data), res) |
296c0905 C |
272 | } |
273 | ||
4b8f09fa | 274 | return activityPubResponse(activityPubContextify(videoCommentObject), res) |
da854ddd | 275 | } |
7006bc63 | 276 | |
c48e82b5 | 277 | async function videoRedundancyController (req: express.Request, res: express.Response) { |
8d1fa36a C |
278 | const videoRedundancy: VideoRedundancyModel = res.locals.videoRedundancy |
279 | if (videoRedundancy.isOwned() === false) return res.redirect(videoRedundancy.url) | |
280 | ||
c48e82b5 C |
281 | const serverActor = await getServerActor() |
282 | ||
283 | const audience = getAudience(serverActor) | |
284 | const object = audiencify(videoRedundancy.toActivityPubObject(), audience) | |
285 | ||
286 | if (req.path.endsWith('/activity')) { | |
287 | const data = buildCreateActivity(videoRedundancy.url, serverActor, object, audience) | |
288 | return activityPubResponse(activityPubContextify(data), res) | |
289 | } | |
290 | ||
291 | return activityPubResponse(activityPubContextify(object), res) | |
292 | } | |
293 | ||
7006bc63 C |
294 | // --------------------------------------------------------------------------- |
295 | ||
296 | async function actorFollowing (req: express.Request, actor: ActorModel) { | |
8fffe21a C |
297 | const handler = (start: number, count: number) => { |
298 | return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) | |
299 | } | |
7006bc63 | 300 | |
babecc3c | 301 | return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.path, handler, req.query.page) |
7006bc63 C |
302 | } |
303 | ||
304 | async function actorFollowers (req: express.Request, actor: ActorModel) { | |
8fffe21a C |
305 | const handler = (start: number, count: number) => { |
306 | return ActorFollowModel.listAcceptedFollowerUrlsForApi([ actor.id ], undefined, start, count) | |
307 | } | |
7006bc63 | 308 | |
babecc3c | 309 | return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.path, handler, req.query.page) |
7006bc63 | 310 | } |
4b8f09fa | 311 | |
8fffe21a C |
312 | function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) { |
313 | const handler = async (start: number, count: number) => { | |
314 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) | |
315 | return { | |
316 | total: result.count, | |
5c6d985f | 317 | data: result.rows.map(r => r.url) |
8fffe21a C |
318 | } |
319 | } | |
320 | return activityPubCollectionPagination(url, handler, req.query.page) | |
4b8f09fa | 321 | } |