aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0175-actor-follow-counts.ts24
-rw-r--r--server/lib/activitypub/actor.ts1
-rw-r--r--server/lib/activitypub/process/process-follow.ts3
-rw-r--r--server/models/activitypub/actor-follow.ts20
-rw-r--r--server/models/activitypub/actor.ts10
-rw-r--r--server/tests/api/server/follows.ts37
-rw-r--r--server/tests/utils/users/accounts.ts12
8 files changed, 108 insertions, 1 deletions
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index eda3403f2..2c64efe1f 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -9,7 +9,7 @@ import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core
9 9
10// --------------------------------------------------------------------------- 10// ---------------------------------------------------------------------------
11 11
12const LAST_MIGRATION_VERSION = 170 12const LAST_MIGRATION_VERSION = 175
13 13
14// --------------------------------------------------------------------------- 14// ---------------------------------------------------------------------------
15 15
diff --git a/server/initializers/migrations/0175-actor-follow-counts.ts b/server/initializers/migrations/0175-actor-follow-counts.ts
new file mode 100644
index 000000000..06ef77b49
--- /dev/null
+++ b/server/initializers/migrations/0175-actor-follow-counts.ts
@@ -0,0 +1,24 @@
1import * as Sequelize from 'sequelize'
2import { ACTOR_FOLLOW_SCORE } from '../index'
3
4async function up (utils: {
5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize
8}): Promise<void> {
9 const query = 'UPDATE "actor" SET ' +
10 '"followersCount" = (SELECT COUNT(*) FROM "actorFollow" WHERE "actor"."id" = "actorFollow"."targetActorId"), ' +
11 '"followingCount" = (SELECT COUNT(*) FROM "actorFollow" WHERE "actor"."id" = "actorFollow"."actorId") ' +
12 'WHERE "actor"."serverId" IS NULL'
13
14 await utils.sequelize.query(query)
15}
16
17function down (options) {
18 throw new Error('Not implemented.')
19}
20
21export {
22 up,
23 down
24}
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index 1e1eab54a..b3fb75421 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -314,6 +314,7 @@ async function refreshActorIfNeeded (actor: ActorModel) {
314 if (result === undefined) throw new Error('Cannot fetch remote actor in refresh actor.') 314 if (result === undefined) throw new Error('Cannot fetch remote actor in refresh actor.')
315 315
316 return sequelizeTypescript.transaction(async t => { 316 return sequelizeTypescript.transaction(async t => {
317 logger.info('coucou', result.actor.toJSON())
317 updateInstanceWithAnother(actor, result.actor) 318 updateInstanceWithAnother(actor, result.actor)
318 319
319 if (result.avatarName !== undefined) { 320 if (result.avatarName !== undefined) {
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index 5085c5da9..69f5c51b5 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -51,6 +51,9 @@ async function follow (actor: ActorModel, targetActorURL: string) {
51 transaction: t 51 transaction: t
52 }) 52 })
53 53
54 actorFollow.ActorFollower = actor
55 actorFollow.ActorFollowing = targetActor
56
54 if (actorFollow.state !== 'accepted') { 57 if (actorFollow.state !== 'accepted') {
55 actorFollow.state = 'accepted' 58 actorFollow.state = 'accepted'
56 await actorFollow.save({ transaction: t }) 59 await actorFollow.save({ transaction: t })
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index de5bb6f74..435d22db5 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -2,6 +2,7 @@ import * as Bluebird from 'bluebird'
2import { values } from 'lodash' 2import { values } from 'lodash'
3import * as Sequelize from 'sequelize' 3import * as Sequelize from 'sequelize'
4import { 4import {
5 AfterCreate, AfterDestroy, AfterUpdate,
5 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table, 6 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table,
6 UpdatedAt 7 UpdatedAt
7} from 'sequelize-typescript' 8} from 'sequelize-typescript'
@@ -79,6 +80,25 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
79 }) 80 })
80 ActorFollowing: ActorModel 81 ActorFollowing: ActorModel
81 82
83 @AfterCreate
84 @AfterUpdate
85 static incrementFollowerAndFollowingCount (instance: ActorFollowModel) {
86 if (instance.state !== 'accepted') return
87
88 return Promise.all([
89 ActorModel.incrementFollows(instance.actorId, 'followingCount', 1),
90 ActorModel.incrementFollows(instance.targetActorId, 'followersCount', 1)
91 ])
92 }
93
94 @AfterDestroy
95 static decrementFollowerAndFollowingCount (instance: ActorFollowModel) {
96 return Promise.all([
97 ActorModel.incrementFollows(instance.actorId, 'followingCount',-1),
98 ActorModel.incrementFollows(instance.targetActorId, 'followersCount', -1)
99 ])
100 }
101
82 // Remove actor follows with a score of 0 (too many requests where they were unreachable) 102 // Remove actor follows with a score of 0 (too many requests where they were unreachable)
83 static async removeBadActorFollows () { 103 static async removeBadActorFollows () {
84 const actorFollows = await ActorFollowModel.listBadActorFollows() 104 const actorFollows = await ActorFollowModel.listBadActorFollows()
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 17f69f7a7..b7be9c32c 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -264,6 +264,16 @@ export class ActorModel extends Model<ActorModel> {
264 return ActorModel.scope(ScopeNames.FULL).findOne(query) 264 return ActorModel.scope(ScopeNames.FULL).findOne(query)
265 } 265 }
266 266
267 static incrementFollows (id: number, column: 'followersCount' | 'followingCount', by: number) {
268 // FIXME: typings
269 return (ActorModel as any).increment(column, {
270 by,
271 where: {
272 id
273 }
274 })
275 }
276
267 toFormattedJSON () { 277 toFormattedJSON () {
268 let avatar: Avatar = null 278 let avatar: Avatar = null
269 if (this.Avatar) { 279 if (this.Avatar) {
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index e6dfd5f62..27cf94985 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -12,6 +12,7 @@ import {
12} from '../../utils/index' 12} from '../../utils/index'
13import { dateIsValid } from '../../utils/miscs/miscs' 13import { dateIsValid } from '../../utils/miscs/miscs'
14import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' 14import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows'
15import { expectAccountFollows } from '../../utils/users/accounts'
15import { userLogin } from '../../utils/users/login' 16import { userLogin } from '../../utils/users/login'
16import { createUser } from '../../utils/users/users' 17import { createUser } from '../../utils/users/users'
17import { 18import {
@@ -116,6 +117,19 @@ describe('Test follows', function () {
116 expect(follows.length).to.equal(0) 117 expect(follows.length).to.equal(0)
117 }) 118 })
118 119
120 it('Should have the correct following counts', async function () {
121 await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2)
122 await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0)
123 await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0)
124
125 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh)
126 await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1)
127 await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0)
128
129 await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1)
130 await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0)
131 })
132
119 it('Should unfollow server 3 on server 1', async function () { 133 it('Should unfollow server 3 on server 1', async function () {
120 this.timeout(5000) 134 this.timeout(5000)
121 135
@@ -144,6 +158,17 @@ describe('Test follows', function () {
144 expect(follows.length).to.equal(0) 158 expect(follows.length).to.equal(0)
145 }) 159 })
146 160
161 it('Should have the correct following counts 2', async function () {
162 await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 1)
163 await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0)
164
165 await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1)
166 await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0)
167
168 await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 0)
169 await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 0, 0)
170 })
171
147 it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () { 172 it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () {
148 this.timeout(10000) 173 this.timeout(10000)
149 174
@@ -223,6 +248,18 @@ describe('Test follows', function () {
223 await wait(7000) 248 await wait(7000)
224 }) 249 })
225 250
251 it('Should have the correct following counts 2', async function () {
252 await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2)
253 await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0)
254 await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0)
255
256 await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1)
257 await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0)
258
259 await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1)
260 await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0)
261 })
262
226 it('Should propagate videos', async function () { 263 it('Should propagate videos', async function () {
227 const res = await getVideosList(servers[ 0 ].url) 264 const res = await getVideosList(servers[ 0 ].url)
228 expect(res.body.total).to.equal(7) 265 expect(res.body.total).to.equal(7)
diff --git a/server/tests/utils/users/accounts.ts b/server/tests/utils/users/accounts.ts
index 71712100e..0ec7992b3 100644
--- a/server/tests/utils/users/accounts.ts
+++ b/server/tests/utils/users/accounts.ts
@@ -1,3 +1,5 @@
1import { expect } from 'chai'
2import { Account } from '../../../../shared/models/actors'
1import { makeGetRequest } from '../requests/requests' 3import { makeGetRequest } from '../requests/requests'
2 4
3function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) { 5function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) {
@@ -21,9 +23,19 @@ function getAccount (url: string, accountId: number | string, statusCodeExpected
21 }) 23 })
22} 24}
23 25
26async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
27 const res = await getAccountsList(url)
28 const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
29
30 const message = `${nameWithDomain} on ${url}`
31 expect(account.followersCount).to.equal(followersCount, message)
32 expect(account.followingCount).to.equal(followingCount, message)
33}
34
24// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
25 36
26export { 37export {
27 getAccount, 38 getAccount,
39 expectAccountFollows,
28 getAccountsList 40 getAccountsList
29} 41}