]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/server/follows.ts
Merge remote-tracking branch 'weblate/develop' into develop
[github/Chocobozzz/PeerTube.git] / server / controllers / api / server / follows.ts
1 import express from 'express'
2 import { getServerActor } from '@server/models/application/application'
3 import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4 import { UserRight } from '../../../../shared/models/users'
5 import { logger } from '../../../helpers/logger'
6 import { getFormattedObjects } from '../../../helpers/utils'
7 import { SERVER_ACTOR_NAME } from '../../../initializers/constants'
8 import { sequelizeTypescript } from '../../../initializers/database'
9 import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow'
10 import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send'
11 import { JobQueue } from '../../../lib/job-queue'
12 import { removeRedundanciesOfServer } from '../../../lib/redundancy'
13 import {
14 asyncMiddleware,
15 authenticate,
16 ensureUserHasRight,
17 paginationValidator,
18 setBodyHostsPort,
19 setDefaultPagination,
20 setDefaultSort
21 } from '../../../middlewares'
22 import {
23 acceptOrRejectFollowerValidator,
24 instanceFollowersSortValidator,
25 instanceFollowingSortValidator,
26 followValidator,
27 getFollowerValidator,
28 listFollowsValidator,
29 removeFollowingValidator
30 } from '../../../middlewares/validators'
31 import { ActorFollowModel } from '../../../models/actor/actor-follow'
32 import { ServerFollowCreate } from '@shared/models'
33
34 const serverFollowsRouter = express.Router()
35 serverFollowsRouter.get('/following',
36 listFollowsValidator,
37 paginationValidator,
38 instanceFollowingSortValidator,
39 setDefaultSort,
40 setDefaultPagination,
41 asyncMiddleware(listFollowing)
42 )
43
44 serverFollowsRouter.post('/following',
45 authenticate,
46 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
47 followValidator,
48 setBodyHostsPort,
49 asyncMiddleware(addFollow)
50 )
51
52 serverFollowsRouter.delete('/following/:hostOrHandle',
53 authenticate,
54 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
55 asyncMiddleware(removeFollowingValidator),
56 asyncMiddleware(removeFollowing)
57 )
58
59 serverFollowsRouter.get('/followers',
60 listFollowsValidator,
61 paginationValidator,
62 instanceFollowersSortValidator,
63 setDefaultSort,
64 setDefaultPagination,
65 asyncMiddleware(listFollowers)
66 )
67
68 serverFollowsRouter.delete('/followers/:nameWithHost',
69 authenticate,
70 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
71 asyncMiddleware(getFollowerValidator),
72 asyncMiddleware(removeOrRejectFollower)
73 )
74
75 serverFollowsRouter.post('/followers/:nameWithHost/reject',
76 authenticate,
77 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
78 asyncMiddleware(getFollowerValidator),
79 acceptOrRejectFollowerValidator,
80 asyncMiddleware(removeOrRejectFollower)
81 )
82
83 serverFollowsRouter.post('/followers/:nameWithHost/accept',
84 authenticate,
85 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
86 asyncMiddleware(getFollowerValidator),
87 acceptOrRejectFollowerValidator,
88 asyncMiddleware(acceptFollower)
89 )
90
91 // ---------------------------------------------------------------------------
92
93 export {
94 serverFollowsRouter
95 }
96
97 // ---------------------------------------------------------------------------
98
99 async function listFollowing (req: express.Request, res: express.Response) {
100 const serverActor = await getServerActor()
101 const resultList = await ActorFollowModel.listInstanceFollowingForApi({
102 followerId: serverActor.id,
103 start: req.query.start,
104 count: req.query.count,
105 sort: req.query.sort,
106 search: req.query.search,
107 actorType: req.query.actorType,
108 state: req.query.state
109 })
110
111 return res.json(getFormattedObjects(resultList.data, resultList.total))
112 }
113
114 async function listFollowers (req: express.Request, res: express.Response) {
115 const serverActor = await getServerActor()
116 const resultList = await ActorFollowModel.listFollowersForApi({
117 actorIds: [ serverActor.id ],
118 start: req.query.start,
119 count: req.query.count,
120 sort: req.query.sort,
121 search: req.query.search,
122 actorType: req.query.actorType,
123 state: req.query.state
124 })
125
126 return res.json(getFormattedObjects(resultList.data, resultList.total))
127 }
128
129 async function addFollow (req: express.Request, res: express.Response) {
130 const { hosts, handles } = req.body as ServerFollowCreate
131 const follower = await getServerActor()
132
133 for (const host of hosts) {
134 const payload = {
135 host,
136 name: SERVER_ACTOR_NAME,
137 followerActorId: follower.id
138 }
139
140 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
141 }
142
143 for (const handle of handles) {
144 const [ name, host ] = handle.split('@')
145
146 const payload = {
147 host,
148 name,
149 followerActorId: follower.id
150 }
151
152 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
153 }
154
155 return res.status(HttpStatusCode.NO_CONTENT_204).end()
156 }
157
158 async function removeFollowing (req: express.Request, res: express.Response) {
159 const follow = res.locals.follow
160
161 await sequelizeTypescript.transaction(async t => {
162 if (follow.state === 'accepted') sendUndoFollow(follow, t)
163
164 // Disable redundancy on unfollowed instances
165 const server = follow.ActorFollowing.Server
166 server.redundancyAllowed = false
167 await server.save({ transaction: t })
168
169 // Async, could be long
170 removeRedundanciesOfServer(server.id)
171 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err))
172
173 await follow.destroy({ transaction: t })
174 })
175
176 return res.status(HttpStatusCode.NO_CONTENT_204).end()
177 }
178
179 async function removeOrRejectFollower (req: express.Request, res: express.Response) {
180 const follow = res.locals.follow
181
182 await sendReject(follow.url, follow.ActorFollower, follow.ActorFollowing)
183
184 await follow.destroy()
185
186 return res.status(HttpStatusCode.NO_CONTENT_204).end()
187 }
188
189 async function acceptFollower (req: express.Request, res: express.Response) {
190 const follow = res.locals.follow
191
192 sendAccept(follow)
193
194 follow.state = 'accepted'
195 await follow.save()
196
197 await autoFollowBackIfNeeded(follow)
198
199 return res.status(HttpStatusCode.NO_CONTENT_204).end()
200 }