aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-04-08 14:04:57 +0200
committerChocobozzz <me@florianbigard.com>2019-04-08 14:06:23 +0200
commit5b9c965d5aa747f29b081289f930ee215fdc23c8 (patch)
tree1c726117525230d74235e7fa986eb72e7376fb6c
parent594d0c6a7c64b045c11508bb4e4b19b75b3fc557 (diff)
downloadPeerTube-5b9c965d5aa747f29b081289f930ee215fdc23c8.tar.gz
PeerTube-5b9c965d5aa747f29b081289f930ee215fdc23c8.tar.zst
PeerTube-5b9c965d5aa747f29b081289f930ee215fdc23c8.zip
Add ability to forbid followers
-rw-r--r--config/default.yaml5
-rw-r--r--config/production.yaml.example9
-rw-r--r--server/controllers/api/config.ts5
-rw-r--r--server/controllers/api/server/follows.ts2
-rw-r--r--server/initializers/checker-before-init.ts3
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/lib/activitypub/process/process-follow.ts10
-rw-r--r--server/lib/activitypub/send/send-accept.ts2
-rw-r--r--server/lib/activitypub/send/send-follow.ts2
-rw-r--r--server/lib/activitypub/send/send-reject.ts18
-rw-r--r--server/lib/activitypub/send/send-undo.ts2
-rw-r--r--server/lib/activitypub/url.ts12
-rw-r--r--server/middlewares/validators/follows.ts15
-rw-r--r--server/tests/api/check-params/config.ts5
-rw-r--r--server/tests/api/check-params/follows.ts40
-rw-r--r--server/tests/api/server/config.ts9
-rw-r--r--server/tests/api/server/follows-moderation.ts96
-rw-r--r--shared/models/server/custom-config.model.ts6
-rw-r--r--shared/utils/server/config.ts5
19 files changed, 196 insertions, 55 deletions
diff --git a/config/default.yaml b/config/default.yaml
index 615910478..51f3ad833 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -200,3 +200,8 @@ services:
200 # If false, we use an image link card that will redirect on your PeerTube instance 200 # If false, we use an image link card that will redirect on your PeerTube instance
201 # Change it to "true", and then test on https://cards-dev.twitter.com/validator to see if you are whitelisted 201 # Change it to "true", and then test on https://cards-dev.twitter.com/validator to see if you are whitelisted
202 whitelisted: false 202 whitelisted: false
203
204followers:
205 instance:
206 # Allow or not other instances to follow yours
207 enabled: true
diff --git a/config/production.yaml.example b/config/production.yaml.example
index 5299484a5..a2811abd6 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -180,8 +180,8 @@ auto_blacklist:
180 # New videos automatically blacklisted so moderators can review before publishing 180 # New videos automatically blacklisted so moderators can review before publishing
181 videos: 181 videos:
182 of_users: 182 of_users:
183 enabled: false 183 enabled: false
184 184
185# Instance settings 185# Instance settings
186instance: 186instance:
187 name: 'PeerTube' 187 name: 'PeerTube'
@@ -217,3 +217,8 @@ services:
217 # If false, we use an image link card that will redirect on your PeerTube instance 217 # If false, we use an image link card that will redirect on your PeerTube instance
218 # Test on https://cards-dev.twitter.com/validator to see if you are whitelisted 218 # Test on https://cards-dev.twitter.com/validator to see if you are whitelisted
219 whitelisted: false 219 whitelisted: false
220
221followers:
222 instance:
223 # Allow or not other instances to follow yours
224 enabled: true
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index bd0ba4f9d..f9bb0b947 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -279,6 +279,11 @@ function customConfig (): CustomConfig {
279 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED 279 enabled: CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED
280 } 280 }
281 } 281 }
282 },
283 followers: {
284 instance: {
285 enabled: CONFIG.FOLLOWERS.INSTANCE.ENABLED
286 }
282 } 287 }
283 } 288 }
284} 289}
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index c00069f93..87cf091cb 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -139,7 +139,7 @@ async function removeFollowing (req: express.Request, res: express.Response) {
139async function removeFollower (req: express.Request, res: express.Response) { 139async function removeFollower (req: express.Request, res: express.Response) {
140 const follow = res.locals.follow 140 const follow = res.locals.follow
141 141
142 await sendReject(follow) 142 await sendReject(follow.ActorFollower, follow.ActorFollowing)
143 143
144 await follow.destroy() 144 await follow.destroy()
145 145
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index e26f38564..a9896907d 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -24,7 +24,8 @@ function checkMissedConfig () {
24 'trending.videos.interval_days', 24 'trending.videos.interval_days',
25 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', 25 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
26 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt', 26 'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
27 'services.twitter.username', 'services.twitter.whitelisted' 27 'services.twitter.username', 'services.twitter.whitelisted',
28 'followers.instance.enabled'
28 ] 29 ]
29 const requiredAlternatives = [ 30 const requiredAlternatives = [
30 [ // set 31 [ // set
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index ac19231d0..43c5ec54c 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -324,6 +324,11 @@ const CONFIG = {
324 get USERNAME () { return config.get<string>('services.twitter.username') }, 324 get USERNAME () { return config.get<string>('services.twitter.username') },
325 get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') } 325 get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') }
326 } 326 }
327 },
328 FOLLOWERS: {
329 INSTANCE: {
330 get ENABLED () { return config.get<boolean>('followers.instance.enabled') }
331 }
327 } 332 }
328} 333}
329 334
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index 0cd537187..cecf09b47 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -1,12 +1,13 @@
1import { ActivityFollow } from '../../../../shared/models/activitypub' 1import { ActivityFollow } from '../../../../shared/models/activitypub'
2import { retryTransactionWrapper } from '../../../helpers/database-utils' 2import { retryTransactionWrapper } from '../../../helpers/database-utils'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript, CONFIG } from '../../../initializers'
5import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 6import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
7import { sendAccept } from '../send' 7import { sendAccept, sendReject } from '../send'
8import { Notifier } from '../../notifier' 8import { Notifier } from '../../notifier'
9import { getAPId } from '../../../helpers/activitypub' 9import { getAPId } from '../../../helpers/activitypub'
10import { getServerActor } from '../../../helpers/utils'
10 11
11async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) { 12async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) {
12 const activityObject = getAPId(activity.object) 13 const activityObject = getAPId(activity.object)
@@ -29,6 +30,11 @@ async function processFollow (actor: ActorModel, targetActorURL: string) {
29 if (!targetActor) throw new Error('Unknown actor') 30 if (!targetActor) throw new Error('Unknown actor')
30 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') 31 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
31 32
33 const serverActor = await getServerActor()
34 if (targetActor.id === serverActor.id && CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
35 return sendReject(actor, targetActor)
36 }
37
32 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ 38 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({
33 where: { 39 where: {
34 actorId: actor.id, 40 actorId: actor.id,
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts
index b6abde13d..388a9ed23 100644
--- a/server/lib/activitypub/send/send-accept.ts
+++ b/server/lib/activitypub/send/send-accept.ts
@@ -17,7 +17,7 @@ async function sendAccept (actorFollow: ActorFollowModel) {
17 17
18 logger.info('Creating job to accept follower %s.', follower.url) 18 logger.info('Creating job to accept follower %s.', follower.url)
19 19
20 const followUrl = getActorFollowActivityPubUrl(actorFollow) 20 const followUrl = getActorFollowActivityPubUrl(follower, me)
21 const followData = buildFollowActivity(followUrl, follower, me) 21 const followData = buildFollowActivity(followUrl, follower, me)
22 22
23 const url = getActorFollowAcceptActivityPubUrl(actorFollow) 23 const url = getActorFollowAcceptActivityPubUrl(actorFollow)
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts
index 170b46b48..2c3d02014 100644
--- a/server/lib/activitypub/send/send-follow.ts
+++ b/server/lib/activitypub/send/send-follow.ts
@@ -14,7 +14,7 @@ function sendFollow (actorFollow: ActorFollowModel) {
14 14
15 logger.info('Creating job to send follow request to %s.', following.url) 15 logger.info('Creating job to send follow request to %s.', following.url)
16 16
17 const url = getActorFollowActivityPubUrl(actorFollow) 17 const url = getActorFollowActivityPubUrl(me, following)
18 const data = buildFollowActivity(url, me, following) 18 const data = buildFollowActivity(url, me, following)
19 19
20 return unicastTo(data, me, following.inboxUrl) 20 return unicastTo(data, me, following.inboxUrl)
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts
index db8c2d86d..bac7ff556 100644
--- a/server/lib/activitypub/send/send-reject.ts
+++ b/server/lib/activitypub/send/send-reject.ts
@@ -1,15 +1,11 @@
1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' 1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
2import { ActorModel } from '../../../models/activitypub/actor' 2import { ActorModel } from '../../../models/activitypub/actor'
3import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 3import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
4import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
5import { unicastTo } from './utils' 4import { unicastTo } from './utils'
6import { buildFollowActivity } from './send-follow' 5import { buildFollowActivity } from './send-follow'
7import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
8 7
9async function sendReject (actorFollow: ActorFollowModel) { 8async function sendReject (follower: ActorModel, following: ActorModel) {
10 const follower = actorFollow.ActorFollower
11 const me = actorFollow.ActorFollowing
12
13 if (!follower.serverId) { // This should never happen 9 if (!follower.serverId) { // This should never happen
14 logger.warn('Do not sending reject to local follower.') 10 logger.warn('Do not sending reject to local follower.')
15 return 11 return
@@ -17,13 +13,13 @@ async function sendReject (actorFollow: ActorFollowModel) {
17 13
18 logger.info('Creating job to reject follower %s.', follower.url) 14 logger.info('Creating job to reject follower %s.', follower.url)
19 15
20 const followUrl = getActorFollowActivityPubUrl(actorFollow) 16 const followUrl = getActorFollowActivityPubUrl(follower, following)
21 const followData = buildFollowActivity(followUrl, follower, me) 17 const followData = buildFollowActivity(followUrl, follower, following)
22 18
23 const url = getActorFollowAcceptActivityPubUrl(actorFollow) 19 const url = getActorFollowRejectActivityPubUrl(follower, following)
24 const data = buildRejectActivity(url, me, followData) 20 const data = buildRejectActivity(url, following, followData)
25 21
26 return unicastTo(data, me, follower.inboxUrl) 22 return unicastTo(data, following, follower.inboxUrl)
27} 23}
28 24
29// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index ecbf605d6..8727a121e 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -31,7 +31,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
31 31
32 logger.info('Creating job to send an unfollow request to %s.', following.url) 32 logger.info('Creating job to send an unfollow request to %s.', following.url)
33 33
34 const followUrl = getActorFollowActivityPubUrl(actorFollow) 34 const followUrl = getActorFollowActivityPubUrl(me, following)
35 const undoUrl = getUndoActivityPubUrl(followUrl) 35 const undoUrl = getUndoActivityPubUrl(followUrl)
36 36
37 const followActivity = buildFollowActivity(followUrl, me, following) 37 const followActivity = buildFollowActivity(followUrl, me, following)
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 7c2ee5bc6..401b83fc2 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -74,11 +74,8 @@ function getVideoDislikesActivityPubUrl (video: VideoModel) {
74 return video.url + '/dislikes' 74 return video.url + '/dislikes'
75} 75}
76 76
77function getActorFollowActivityPubUrl (actorFollow: ActorFollowModel) { 77function getActorFollowActivityPubUrl (follower: ActorModel, following: ActorModel) {
78 const me = actorFollow.ActorFollower 78 return follower.url + '/follows/' + following.id
79 const following = actorFollow.ActorFollowing
80
81 return me.url + '/follows/' + following.id
82} 79}
83 80
84function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) { 81function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) {
@@ -88,6 +85,10 @@ function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) {
88 return follower.url + '/accepts/follows/' + me.id 85 return follower.url + '/accepts/follows/' + me.id
89} 86}
90 87
88function getActorFollowRejectActivityPubUrl (follower: ActorModel, following: ActorModel) {
89 return follower.url + '/rejects/follows/' + following.id
90}
91
91function getVideoAnnounceActivityPubUrl (byActor: ActorModel, video: VideoModel) { 92function getVideoAnnounceActivityPubUrl (byActor: ActorModel, video: VideoModel) {
92 return video.url + '/announces/' + byActor.id 93 return video.url + '/announces/' + byActor.id
93} 94}
@@ -120,6 +121,7 @@ export {
120 getVideoViewActivityPubUrl, 121 getVideoViewActivityPubUrl,
121 getVideoLikeActivityPubUrl, 122 getVideoLikeActivityPubUrl,
122 getVideoDislikeActivityPubUrl, 123 getVideoDislikeActivityPubUrl,
124 getActorFollowRejectActivityPubUrl,
123 getVideoCommentActivityPubUrl, 125 getVideoCommentActivityPubUrl,
124 getDeleteActivityPubUrl, 126 getDeleteActivityPubUrl,
125 getVideoSharesActivityPubUrl, 127 getVideoSharesActivityPubUrl,
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index ef4151efe..38df39fda 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -9,7 +9,6 @@ import { ActorFollowModel } from '../../models/activitypub/actor-follow'
9import { areValidationErrors } from './utils' 9import { areValidationErrors } from './utils'
10import { ActorModel } from '../../models/activitypub/actor' 10import { ActorModel } from '../../models/activitypub/actor'
11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' 11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
12import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub'
13import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
14 13
15const followValidator = [ 14const followValidator = [
@@ -66,12 +65,16 @@ const removeFollowerValidator = [
66 65
67 if (areValidationErrors(req, res)) return 66 if (areValidationErrors(req, res)) return
68 67
69 const serverActor = await getServerActor() 68 let follow: ActorFollowModel
70 69 try {
71 const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost) 70 const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
72 const actor = await ActorModel.loadByUrl(actorUrl) 71 const actor = await ActorModel.loadByUrl(actorUrl)
73 72
74 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, serverActor.id) 73 const serverActor = await getServerActor()
74 follow = await ActorFollowModel.loadByActorAndTarget(actor.id, serverActor.id)
75 } catch (err) {
76 logger.warn('Cannot get actor from handle.', { handle: req.params.nameWithHost, err })
77 }
75 78
76 if (!follow) { 79 if (!follow) {
77 return res 80 return res
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index 0b333e2f4..d117f26e6 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -87,6 +87,11 @@ describe('Test config API validators', function () {
87 enabled: false 87 enabled: false
88 } 88 }
89 } 89 }
90 },
91 followers: {
92 instance: {
93 enabled: false
94 }
90 } 95 }
91 } 96 }
92 97
diff --git a/server/tests/api/check-params/follows.ts b/server/tests/api/check-params/follows.ts
index 2ad1575a3..67fa43778 100644
--- a/server/tests/api/check-params/follows.ts
+++ b/server/tests/api/check-params/follows.ts
@@ -144,6 +144,46 @@ describe('Test server follows API validators', function () {
144 }) 144 })
145 }) 145 })
146 146
147 describe('When removing a follower', function () {
148 const path = '/api/v1/server/followers'
149
150 it('Should fail with an invalid token', async function () {
151 await makeDeleteRequest({
152 url: server.url,
153 path: path + '/toto@localhost:9002',
154 token: 'fake_token',
155 statusCodeExpected: 401
156 })
157 })
158
159 it('Should fail if the user is not an administrator', async function () {
160 await makeDeleteRequest({
161 url: server.url,
162 path: path + '/toto@localhost:9002',
163 token: userAccessToken,
164 statusCodeExpected: 403
165 })
166 })
167
168 it('Should fail with an invalid follower', async function () {
169 await makeDeleteRequest({
170 url: server.url,
171 path: path + '/toto',
172 token: server.accessToken,
173 statusCodeExpected: 400
174 })
175 })
176
177 it('Should fail with an unknown follower', async function () {
178 await makeDeleteRequest({
179 url: server.url,
180 path: path + '/toto@localhost:9003',
181 token: server.accessToken,
182 statusCodeExpected: 404
183 })
184 })
185 })
186
147 describe('When removing following', function () { 187 describe('When removing following', function () {
148 const path = '/api/v1/server/following' 188 const path = '/api/v1/server/following'
149 189
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index b9f05e952..cb2700f29 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -63,6 +63,8 @@ function checkInitialConfig (data: CustomConfig) {
63 expect(data.import.videos.http.enabled).to.be.true 63 expect(data.import.videos.http.enabled).to.be.true
64 expect(data.import.videos.torrent.enabled).to.be.true 64 expect(data.import.videos.torrent.enabled).to.be.true
65 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.false 65 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.false
66
67 expect(data.followers.instance.enabled).to.be.true
66} 68}
67 69
68function checkUpdatedConfig (data: CustomConfig) { 70function checkUpdatedConfig (data: CustomConfig) {
@@ -105,6 +107,8 @@ function checkUpdatedConfig (data: CustomConfig) {
105 expect(data.import.videos.http.enabled).to.be.false 107 expect(data.import.videos.http.enabled).to.be.false
106 expect(data.import.videos.torrent.enabled).to.be.false 108 expect(data.import.videos.torrent.enabled).to.be.false
107 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.true 109 expect(data.autoBlacklist.videos.ofUsers.enabled).to.be.true
110
111 expect(data.followers.instance.enabled).to.be.false
108} 112}
109 113
110describe('Test config', function () { 114describe('Test config', function () {
@@ -234,6 +238,11 @@ describe('Test config', function () {
234 enabled: true 238 enabled: true
235 } 239 }
236 } 240 }
241 },
242 followers: {
243 instance: {
244 enabled: false
245 }
237 } 246 }
238 } 247 }
239 await updateCustomConfig(server.url, server.accessToken, newCustomConfig) 248 await updateCustomConfig(server.url, server.accessToken, newCustomConfig)
diff --git a/server/tests/api/server/follows-moderation.ts b/server/tests/api/server/follows-moderation.ts
index b1cbfb62c..a360706f2 100644
--- a/server/tests/api/server/follows-moderation.ts
+++ b/server/tests/api/server/follows-moderation.ts
@@ -2,7 +2,13 @@
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { flushAndRunMultipleServers, killallServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/utils/index' 5import {
6 flushAndRunMultipleServers,
7 killallServers,
8 ServerInfo,
9 setAccessTokensToServers,
10 updateCustomSubConfig
11} from '../../../../shared/utils/index'
6import { 12import {
7 follow, 13 follow,
8 getFollowersListPaginationAndSort, 14 getFollowersListPaginationAndSort,
@@ -14,6 +20,38 @@ import { ActorFollow } from '../../../../shared/models/actors'
14 20
15const expect = chai.expect 21const expect = chai.expect
16 22
23async function checkHasFollowers (servers: ServerInfo[]) {
24 {
25 const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
26 expect(res.body.total).to.equal(1)
27
28 const follow = res.body.data[0] as ActorFollow
29 expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube')
30 expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube')
31 }
32
33 {
34 const res = await getFollowersListPaginationAndSort(servers[1].url, 0, 5, 'createdAt')
35 expect(res.body.total).to.equal(1)
36
37 const follow = res.body.data[0] as ActorFollow
38 expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube')
39 expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube')
40 }
41}
42
43async function checkNoFollowers (servers: ServerInfo[]) {
44 {
45 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, 'createdAt')
46 expect(res.body.total).to.equal(0)
47 }
48
49 {
50 const res = await getFollowersListPaginationAndSort(servers[ 1 ].url, 0, 5, 'createdAt')
51 expect(res.body.total).to.equal(0)
52 }
53}
54
17describe('Test follows moderation', function () { 55describe('Test follows moderation', function () {
18 let servers: ServerInfo[] = [] 56 let servers: ServerInfo[] = []
19 57
@@ -35,23 +73,7 @@ describe('Test follows moderation', function () {
35 }) 73 })
36 74
37 it('Should have correct follows', async function () { 75 it('Should have correct follows', async function () {
38 { 76 await checkHasFollowers(servers)
39 const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
40 expect(res.body.total).to.equal(1)
41
42 const follow = res.body.data[0] as ActorFollow
43 expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube')
44 expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube')
45 }
46
47 {
48 const res = await getFollowersListPaginationAndSort(servers[1].url, 0, 5, 'createdAt')
49 expect(res.body.total).to.equal(1)
50
51 const follow = res.body.data[0] as ActorFollow
52 expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube')
53 expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube')
54 }
55 }) 77 })
56 78
57 it('Should remove follower on server 2', async function () { 79 it('Should remove follower on server 2', async function () {
@@ -61,15 +83,41 @@ describe('Test follows moderation', function () {
61 }) 83 })
62 84
63 it('Should not not have follows anymore', async function () { 85 it('Should not not have follows anymore', async function () {
64 { 86 await checkNoFollowers(servers)
65 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt') 87 })
66 expect(res.body.total).to.equal(0) 88
89 it('Should disable followers on server 2', async function () {
90 const subConfig = {
91 followers: {
92 instance: {
93 enabled: false
94 }
95 }
67 } 96 }
68 97
69 { 98 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig)
70 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt') 99
71 expect(res.body.total).to.equal(0) 100 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken)
101 await waitJobs(servers)
102
103 await checkNoFollowers(servers)
104 })
105
106 it('Should re enable followers on server 2', async function () {
107 const subConfig = {
108 followers: {
109 instance: {
110 enabled: true
111 }
112 }
72 } 113 }
114
115 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig)
116
117 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken)
118 await waitJobs(servers)
119
120 await checkHasFollowers(servers)
73 }) 121 })
74 122
75 after(async function () { 123 after(async function () {
diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts
index 1607b40a8..642ffea39 100644
--- a/shared/models/server/custom-config.model.ts
+++ b/shared/models/server/custom-config.model.ts
@@ -86,4 +86,10 @@ export interface CustomConfig {
86 } 86 }
87 } 87 }
88 88
89 followers: {
90 instance: {
91 enabled: boolean
92 }
93 }
94
89} 95}
diff --git a/shared/utils/server/config.ts b/shared/utils/server/config.ts
index eaa493a93..21c689714 100644
--- a/shared/utils/server/config.ts
+++ b/shared/utils/server/config.ts
@@ -119,6 +119,11 @@ function updateCustomSubConfig (url: string, token: string, newConfig: any) {
119 enabled: false 119 enabled: false
120 } 120 }
121 } 121 }
122 },
123 followers: {
124 instance: {
125 enabled: true
126 }
122 } 127 }
123 } 128 }
124 129