aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-16 15:25:20 +0200
committerChocobozzz <me@florianbigard.com>2018-08-27 09:41:54 +0200
commit06a05d5f4784a7cbb27aa1188385b5679845dad8 (patch)
treeac197f3ed0768529456225ad76c912f22bc55e29 /server/controllers
parent4bda2e47bbc937c401ddcf14c1be53c70481a294 (diff)
downloadPeerTube-06a05d5f4784a7cbb27aa1188385b5679845dad8.tar.gz
PeerTube-06a05d5f4784a7cbb27aa1188385b5679845dad8.tar.zst
PeerTube-06a05d5f4784a7cbb27aa1188385b5679845dad8.zip
Add subscriptions endpoints to REST API
Diffstat (limited to 'server/controllers')
-rw-r--r--server/controllers/activitypub/inbox.ts20
-rw-r--r--server/controllers/activitypub/outbox.ts14
-rw-r--r--server/controllers/api/accounts.ts1
-rw-r--r--server/controllers/api/search.ts5
-rw-r--r--server/controllers/api/server/follows.ts17
-rw-r--r--server/controllers/api/users/index.ts3
-rw-r--r--server/controllers/api/users/me.ts114
-rw-r--r--server/controllers/api/video-channel.ts1
-rw-r--r--server/controllers/api/videos/index.ts1
-rw-r--r--server/controllers/feeds.ts1
10 files changed, 148 insertions, 29 deletions
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index a106df717..20bd20ed4 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -3,9 +3,10 @@ import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActi
3import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' 3import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { processActivities } from '../../lib/activitypub/process/process' 5import { processActivities } from '../../lib/activitypub/process/process'
6import { asyncMiddleware, checkSignature, localAccountValidator, signatureValidator } from '../../middlewares' 6import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares'
7import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' 7import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
8import { ActorModel } from '../../models/activitypub/actor' 8import { VideoChannelModel } from '../../models/video/video-channel'
9import { AccountModel } from '../../models/account/account'
9 10
10const inboxRouter = express.Router() 11const inboxRouter = express.Router()
11 12
@@ -23,6 +24,13 @@ inboxRouter.post('/accounts/:name/inbox',
23 asyncMiddleware(activityPubValidator), 24 asyncMiddleware(activityPubValidator),
24 asyncMiddleware(inboxController) 25 asyncMiddleware(inboxController)
25) 26)
27inboxRouter.post('/video-channels/:name/inbox',
28 signatureValidator,
29 asyncMiddleware(checkSignature),
30 asyncMiddleware(localVideoChannelValidator),
31 asyncMiddleware(activityPubValidator),
32 asyncMiddleware(inboxController)
33)
26 34
27// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
28 36
@@ -49,16 +57,16 @@ async function inboxController (req: express.Request, res: express.Response, nex
49 activities = activities.filter(a => isActivityValid(a)) 57 activities = activities.filter(a => isActivityValid(a))
50 logger.debug('We keep %d activities.', activities.length, { activities }) 58 logger.debug('We keep %d activities.', activities.length, { activities })
51 59
52 let specificActor: ActorModel = undefined 60 let accountOrChannel: VideoChannelModel | AccountModel
53 if (res.locals.account) { 61 if (res.locals.account) {
54 specificActor = res.locals.account 62 accountOrChannel = res.locals.account
55 } else if (res.locals.videoChannel) { 63 } else if (res.locals.videoChannel) {
56 specificActor = res.locals.videoChannel 64 accountOrChannel = res.locals.videoChannel
57 } 65 }
58 66
59 logger.info('Receiving inbox requests for %d activities by %s.', activities.length, res.locals.signature.actor.url) 67 logger.info('Receiving inbox requests for %d activities by %s.', activities.length, res.locals.signature.actor.url)
60 68
61 await processActivities(activities, res.locals.signature.actor, specificActor) 69 await processActivities(activities, res.locals.signature.actor, accountOrChannel ? accountOrChannel.Actor : undefined)
62 70
63 res.status(204).end() 71 res.status(204).end()
64} 72}
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index ae7adcd4c..db69ae54b 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -5,11 +5,12 @@ import { activityPubCollectionPagination, activityPubContextify } from '../../he
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
6import { announceActivityData, createActivityData } from '../../lib/activitypub/send' 6import { announceActivityData, createActivityData } from '../../lib/activitypub/send'
7import { buildAudience } from '../../lib/activitypub/audience' 7import { buildAudience } from '../../lib/activitypub/audience'
8import { asyncMiddleware, localAccountValidator } from '../../middlewares' 8import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares'
9import { AccountModel } from '../../models/account/account' 9import { AccountModel } from '../../models/account/account'
10import { ActorModel } from '../../models/activitypub/actor' 10import { ActorModel } from '../../models/activitypub/actor'
11import { VideoModel } from '../../models/video/video' 11import { VideoModel } from '../../models/video/video'
12import { activityPubResponse } from './utils' 12import { activityPubResponse } from './utils'
13import { VideoChannelModel } from '../../models/video/video-channel'
13 14
14const outboxRouter = express.Router() 15const outboxRouter = express.Router()
15 16
@@ -18,6 +19,11 @@ outboxRouter.get('/accounts/:name/outbox',
18 asyncMiddleware(outboxController) 19 asyncMiddleware(outboxController)
19) 20)
20 21
22outboxRouter.get('/video-channels/:name/outbox',
23 localVideoChannelValidator,
24 asyncMiddleware(outboxController)
25)
26
21// --------------------------------------------------------------------------- 27// ---------------------------------------------------------------------------
22 28
23export { 29export {
@@ -27,9 +33,9 @@ export {
27// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
28 34
29async function outboxController (req: express.Request, res: express.Response, next: express.NextFunction) { 35async function outboxController (req: express.Request, res: express.Response, next: express.NextFunction) {
30 const account: AccountModel = res.locals.account 36 const accountOrVideoChannel: AccountModel | VideoChannelModel = res.locals.account || res.locals.videoChannel
31 const actor = account.Actor 37 const actor = accountOrVideoChannel.Actor
32 const actorOutboxUrl = account.Actor.url + '/outbox' 38 const actorOutboxUrl = actor.url + '/outbox'
33 39
34 logger.info('Receiving outbox request for %s.', actorOutboxUrl) 40 logger.info('Receiving outbox request for %s.', actorOutboxUrl)
35 41
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 0117fc8c6..308970abc 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -78,6 +78,7 @@ async function listAccountVideos (req: express.Request, res: express.Response, n
78 start: req.query.start, 78 start: req.query.start,
79 count: req.query.count, 79 count: req.query.count,
80 sort: req.query.sort, 80 sort: req.query.sort,
81 includeLocalVideos: false,
81 categoryOneOf: req.query.categoryOneOf, 82 categoryOneOf: req.query.categoryOneOf,
82 licenceOneOf: req.query.licenceOneOf, 83 licenceOneOf: req.query.licenceOneOf,
83 languageOneOf: req.query.languageOneOf, 84 languageOneOf: req.query.languageOneOf,
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts
index f810c7452..7a7504b7d 100644
--- a/server/controllers/api/search.ts
+++ b/server/controllers/api/search.ts
@@ -36,7 +36,10 @@ export { searchRouter }
36async function searchVideos (req: express.Request, res: express.Response) { 36async function searchVideos (req: express.Request, res: express.Response) {
37 const query: VideosSearchQuery = req.query 37 const query: VideosSearchQuery = req.query
38 38
39 const options = Object.assign(query, { nsfw: buildNSFWFilter(res, query.nsfw) }) 39 const options = Object.assign(query, {
40 includeLocalVideos: true,
41 nsfw: buildNSFWFilter(res, query.nsfw)
42 })
40 const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) 43 const resultList = await VideoModel.searchAndPopulateAccountAndServer(options)
41 44
42 return res.json(getFormattedObjects(resultList.data, resultList.total)) 45 return res.json(getFormattedObjects(resultList.data, resultList.total))
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index e78361c9a..23308445f 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { UserRight } from '../../../../shared/models/users' 2import { UserRight } from '../../../../shared/models/users'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getFormattedObjects, getServerActor } from '../../../helpers/utils' 4import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
5import { sequelizeTypescript } from '../../../initializers' 5import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../../../initializers'
6import { sendUndoFollow } from '../../../lib/activitypub/send' 6import { sendUndoFollow } from '../../../lib/activitypub/send'
7import { 7import {
8 asyncMiddleware, 8 asyncMiddleware,
@@ -74,9 +74,16 @@ async function listFollowers (req: express.Request, res: express.Response, next:
74 74
75async function followInstance (req: express.Request, res: express.Response, next: express.NextFunction) { 75async function followInstance (req: express.Request, res: express.Response, next: express.NextFunction) {
76 const hosts = req.body.hosts as string[] 76 const hosts = req.body.hosts as string[]
77 const follower = await getServerActor()
77 78
78 for (const host of hosts) { 79 for (const host of hosts) {
79 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload: { host } }) 80 const payload = {
81 host,
82 name: SERVER_ACTOR_NAME,
83 followerActorId: follower.id
84 }
85
86 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
80 .catch(err => logger.error('Cannot create follow job for %s.', host, err)) 87 .catch(err => logger.error('Cannot create follow job for %s.', host, err))
81 } 88 }
82 89
@@ -92,11 +99,5 @@ async function removeFollow (req: express.Request, res: express.Response, next:
92 await follow.destroy({ transaction: t }) 99 await follow.destroy({ transaction: t })
93 }) 100 })
94 101
95 // Destroy the actor that will destroy video channels, videos and video files too
96 // This could be long so don't wait this task
97 const following = follow.ActorFollowing
98 following.destroy()
99 .catch(err => logger.error('Cannot destroy actor that we do not follow anymore %s.', following.url, { err }))
100
101 return res.status(204).end() 102 return res.status(204).end()
102} 103}
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index 105244ddd..608d439ac 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -29,7 +29,6 @@ import { usersAskResetPasswordValidator, usersBlockingValidator, usersResetPassw
29import { UserModel } from '../../../models/account/user' 29import { UserModel } from '../../../models/account/user'
30import { OAuthTokenModel } from '../../../models/oauth/oauth-token' 30import { OAuthTokenModel } from '../../../models/oauth/oauth-token'
31import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger' 31import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
32import { videosRouter } from '../videos'
33import { meRouter } from './me' 32import { meRouter } from './me'
34 33
35const auditLogger = auditLoggerFactory('users') 34const auditLogger = auditLoggerFactory('users')
@@ -41,7 +40,7 @@ const loginRateLimiter = new RateLimit({
41}) 40})
42 41
43const usersRouter = express.Router() 42const usersRouter = express.Router()
44videosRouter.use('/', meRouter) 43usersRouter.use('/', meRouter)
45 44
46usersRouter.get('/', 45usersRouter.get('/',
47 authenticate, 46 authenticate,
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 1e096a35d..403842163 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -7,23 +7,35 @@ import { sendUpdateActor } from '../../../lib/activitypub/send'
7import { 7import {
8 asyncMiddleware, 8 asyncMiddleware,
9 authenticate, 9 authenticate,
10 commonVideosFiltersValidator,
10 paginationValidator, 11 paginationValidator,
11 setDefaultPagination, 12 setDefaultPagination,
12 setDefaultSort, 13 setDefaultSort,
14 userSubscriptionAddValidator,
15 userSubscriptionRemoveValidator,
13 usersUpdateMeValidator, 16 usersUpdateMeValidator,
14 usersVideoRatingValidator 17 usersVideoRatingValidator
15} from '../../../middlewares' 18} from '../../../middlewares'
16import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' 19import {
20 deleteMeValidator,
21 userSubscriptionsSortValidator,
22 videoImportsSortValidator,
23 videosSortValidator
24} from '../../../middlewares/validators'
17import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 25import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
18import { UserModel } from '../../../models/account/user' 26import { UserModel } from '../../../models/account/user'
19import { VideoModel } from '../../../models/video/video' 27import { VideoModel } from '../../../models/video/video'
20import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type' 28import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type'
21import { createReqFiles } from '../../../helpers/express-utils' 29import { buildNSFWFilter, createReqFiles } from '../../../helpers/express-utils'
22import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' 30import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model'
23import { updateAvatarValidator } from '../../../middlewares/validators/avatar' 31import { updateAvatarValidator } from '../../../middlewares/validators/avatar'
24import { updateActorAvatarFile } from '../../../lib/avatar' 32import { updateActorAvatarFile } from '../../../lib/avatar'
25import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger' 33import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
26import { VideoImportModel } from '../../../models/video/video-import' 34import { VideoImportModel } from '../../../models/video/video-import'
35import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
36import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
37import { JobQueue } from '../../../lib/job-queue'
38import { logger } from '../../../helpers/logger'
27 39
28const auditLogger = auditLoggerFactory('users-me') 40const auditLogger = auditLoggerFactory('users-me')
29 41
@@ -83,6 +95,40 @@ meRouter.post('/me/avatar/pick',
83 asyncMiddleware(updateMyAvatar) 95 asyncMiddleware(updateMyAvatar)
84) 96)
85 97
98// ##### Subscriptions part #####
99
100meRouter.get('/me/subscriptions',
101 authenticate,
102 paginationValidator,
103 userSubscriptionsSortValidator,
104 setDefaultSort,
105 setDefaultPagination,
106 asyncMiddleware(getUserSubscriptions)
107)
108
109meRouter.post('/me/subscriptions',
110 authenticate,
111 userSubscriptionAddValidator,
112 asyncMiddleware(addUserSubscription)
113)
114
115meRouter.delete('/me/subscriptions/:uri',
116 authenticate,
117 userSubscriptionRemoveValidator,
118 asyncMiddleware(deleteUserSubscription)
119)
120
121meRouter.get('/me/subscriptions/videos',
122 authenticate,
123 authenticate,
124 paginationValidator,
125 videosSortValidator,
126 setDefaultSort,
127 setDefaultPagination,
128 commonVideosFiltersValidator,
129 asyncMiddleware(getUserSubscriptionVideos)
130)
131
86// --------------------------------------------------------------------------- 132// ---------------------------------------------------------------------------
87 133
88export { 134export {
@@ -91,6 +137,62 @@ export {
91 137
92// --------------------------------------------------------------------------- 138// ---------------------------------------------------------------------------
93 139
140async function addUserSubscription (req: express.Request, res: express.Response) {
141 const user = res.locals.oauth.token.User as UserModel
142 const [ name, host ] = req.body.uri.split('@')
143
144 const payload = {
145 name,
146 host,
147 followerActorId: user.Account.Actor.id
148 }
149
150 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
151 .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err))
152
153 return res.status(204).end()
154}
155
156async function deleteUserSubscription (req: express.Request, res: express.Response) {
157 const subscription: ActorFollowModel = res.locals.subscription
158
159 await sequelizeTypescript.transaction(async t => {
160 return subscription.destroy({ transaction: t })
161 })
162
163 return res.type('json').status(204).end()
164}
165
166async function getUserSubscriptions (req: express.Request, res: express.Response) {
167 const user = res.locals.oauth.token.User as UserModel
168 const actorId = user.Account.Actor.id
169
170 const resultList = await ActorFollowModel.listSubscriptionsForApi(actorId, req.query.start, req.query.count, req.query.sort)
171
172 return res.json(getFormattedObjects(resultList.data, resultList.total))
173}
174
175async function getUserSubscriptionVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
176 const user = res.locals.oauth.token.User as UserModel
177 const resultList = await VideoModel.listForApi({
178 start: req.query.start,
179 count: req.query.count,
180 sort: req.query.sort,
181 includeLocalVideos: false,
182 categoryOneOf: req.query.categoryOneOf,
183 licenceOneOf: req.query.licenceOneOf,
184 languageOneOf: req.query.languageOneOf,
185 tagsOneOf: req.query.tagsOneOf,
186 tagsAllOf: req.query.tagsAllOf,
187 nsfw: buildNSFWFilter(res, req.query.nsfw),
188 filter: req.query.filter as VideoFilter,
189 withFiles: false,
190 actorId: user.Account.Actor.id
191 })
192
193 return res.json(getFormattedObjects(resultList.data, resultList.total))
194}
195
94async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 196async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
95 const user = res.locals.oauth.token.User as UserModel 197 const user = res.locals.oauth.token.User as UserModel
96 const resultList = await VideoModel.listUserVideosForApi( 198 const resultList = await VideoModel.listUserVideosForApi(
@@ -150,7 +252,7 @@ async function getUserVideoRating (req: express.Request, res: express.Response,
150 videoId, 252 videoId,
151 rating 253 rating
152 } 254 }
153 res.json(json) 255 return res.json(json)
154} 256}
155 257
156async function deleteMe (req: express.Request, res: express.Response) { 258async function deleteMe (req: express.Request, res: express.Response) {
@@ -207,9 +309,5 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
207 oldUserAuditView 309 oldUserAuditView
208 ) 310 )
209 311
210 return res 312 return res.json({ avatar: avatar.toFormattedJSON() })
211 .json({
212 avatar: avatar.toFormattedJSON()
213 })
214 .end()
215} 313}
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 023ebbedf..6ffc09f87 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -215,6 +215,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
215 start: req.query.start, 215 start: req.query.start,
216 count: req.query.count, 216 count: req.query.count,
217 sort: req.query.sort, 217 sort: req.query.sort,
218 includeLocalVideos: false,
218 categoryOneOf: req.query.categoryOneOf, 219 categoryOneOf: req.query.categoryOneOf,
219 licenceOneOf: req.query.licenceOneOf, 220 licenceOneOf: req.query.licenceOneOf,
220 languageOneOf: req.query.languageOneOf, 221 languageOneOf: req.query.languageOneOf,
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 92c6ee697..e973aa43f 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -414,6 +414,7 @@ async function listVideos (req: express.Request, res: express.Response, next: ex
414 start: req.query.start, 414 start: req.query.start,
415 count: req.query.count, 415 count: req.query.count,
416 sort: req.query.sort, 416 sort: req.query.sort,
417 includeLocalVideos: true,
417 categoryOneOf: req.query.categoryOneOf, 418 categoryOneOf: req.query.categoryOneOf,
418 licenceOneOf: req.query.licenceOneOf, 419 licenceOneOf: req.query.licenceOneOf,
419 languageOneOf: req.query.languageOneOf, 420 languageOneOf: req.query.languageOneOf,
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 682f4abda..b30ad8e8d 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -96,6 +96,7 @@ async function generateVideoFeed (req: express.Request, res: express.Response, n
96 start, 96 start,
97 count: FEEDS.COUNT, 97 count: FEEDS.COUNT,
98 sort: req.query.sort, 98 sort: req.query.sort,
99 includeLocalVideos: true,
99 nsfw, 100 nsfw,
100 filter: req.query.filter, 101 filter: req.query.filter,
101 withFiles: true, 102 withFiles: true,