diff options
author | Chocobozzz <me@florianbigard.com> | 2018-01-11 09:35:50 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-01-11 09:35:50 +0100 |
commit | 60650c77c8a2a98e92d869b237ae4900f369a8fc (patch) | |
tree | 7304a7591b5b23b99d219c4d06c6bd5c4c58c1c2 /server/models | |
parent | 7ae71355c40e9065f83d3fc77b6750d1929ac201 (diff) | |
download | PeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.tar.gz PeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.tar.zst PeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.zip |
Add scores to follows and remove bad ones
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/account/account.ts | 1 | ||||
-rw-r--r-- | server/models/activitypub/actor-follow.ts | 79 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 13 | ||||
-rw-r--r-- | server/models/server/server.ts | 85 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 2 |
5 files changed, 84 insertions, 96 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 47336d1e0..f81c50180 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -179,7 +179,6 @@ export class AccountModel extends Model<AccountModel> { | |||
179 | const actor = this.Actor.toFormattedJSON() | 179 | const actor = this.Actor.toFormattedJSON() |
180 | const account = { | 180 | const account = { |
181 | id: this.id, | 181 | id: this.id, |
182 | name: this.Actor.preferredUsername, | ||
183 | displayName: this.name, | 182 | displayName: this.name, |
184 | createdAt: this.createdAt, | 183 | createdAt: this.createdAt, |
185 | updatedAt: this.updatedAt | 184 | updatedAt: this.updatedAt |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index 5fcc3449d..78a65a0ff 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts | |||
@@ -1,8 +1,14 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { values } from 'lodash' | 2 | import { values } from 'lodash' |
3 | import * as Sequelize from 'sequelize' | 3 | import * as Sequelize from 'sequelize' |
4 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 4 | import { |
5 | AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table, | ||
6 | UpdatedAt | ||
7 | } from 'sequelize-typescript' | ||
5 | import { FollowState } from '../../../shared/models/actors' | 8 | import { FollowState } from '../../../shared/models/actors' |
9 | import { AccountFollow } from '../../../shared/models/actors/follow.model' | ||
10 | import { logger } from '../../helpers/logger' | ||
11 | import { ACTOR_FOLLOW_SCORE } from '../../initializers' | ||
6 | import { FOLLOW_STATES } from '../../initializers/constants' | 12 | import { FOLLOW_STATES } from '../../initializers/constants' |
7 | import { ServerModel } from '../server/server' | 13 | import { ServerModel } from '../server/server' |
8 | import { getSort } from '../utils' | 14 | import { getSort } from '../utils' |
@@ -20,6 +26,9 @@ import { ActorModel } from './actor' | |||
20 | { | 26 | { |
21 | fields: [ 'actorId', 'targetActorId' ], | 27 | fields: [ 'actorId', 'targetActorId' ], |
22 | unique: true | 28 | unique: true |
29 | }, | ||
30 | { | ||
31 | fields: [ 'score' ] | ||
23 | } | 32 | } |
24 | ] | 33 | ] |
25 | }) | 34 | }) |
@@ -29,6 +38,13 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
29 | @Column(DataType.ENUM(values(FOLLOW_STATES))) | 38 | @Column(DataType.ENUM(values(FOLLOW_STATES))) |
30 | state: FollowState | 39 | state: FollowState |
31 | 40 | ||
41 | @AllowNull(false) | ||
42 | @Default(ACTOR_FOLLOW_SCORE.BASE) | ||
43 | @IsInt | ||
44 | @Max(ACTOR_FOLLOW_SCORE.MAX) | ||
45 | @Column | ||
46 | score: number | ||
47 | |||
32 | @CreatedAt | 48 | @CreatedAt |
33 | createdAt: Date | 49 | createdAt: Date |
34 | 50 | ||
@@ -63,6 +79,34 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
63 | }) | 79 | }) |
64 | ActorFollowing: ActorModel | 80 | ActorFollowing: ActorModel |
65 | 81 | ||
82 | // Remove actor follows with a score of 0 (too many requests where they were unreachable) | ||
83 | static async removeBadActorFollows () { | ||
84 | const actorFollows = await ActorFollowModel.listBadActorFollows() | ||
85 | |||
86 | const actorFollowsRemovePromises = actorFollows.map(actorFollow => actorFollow.destroy()) | ||
87 | await Promise.all(actorFollowsRemovePromises) | ||
88 | |||
89 | const numberOfActorFollowsRemoved = actorFollows.length | ||
90 | |||
91 | if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) | ||
92 | } | ||
93 | |||
94 | static updateActorFollowsScoreAndRemoveBadOnes (goodInboxes: string[], badInboxes: string[], t: Sequelize.Transaction) { | ||
95 | if (goodInboxes.length === 0 && badInboxes.length === 0) return | ||
96 | |||
97 | logger.info('Updating %d good actor follows and %d bad actor follows scores.', goodInboxes.length, badInboxes.length) | ||
98 | |||
99 | if (goodInboxes.length !== 0) { | ||
100 | ActorFollowModel.incrementScores(goodInboxes, ACTOR_FOLLOW_SCORE.BONUS, t) | ||
101 | .catch(err => logger.error('Cannot increment scores of good actor follows.', err)) | ||
102 | } | ||
103 | |||
104 | if (badInboxes.length !== 0) { | ||
105 | ActorFollowModel.incrementScores(badInboxes, ACTOR_FOLLOW_SCORE.PENALTY, t) | ||
106 | .catch(err => logger.error('Cannot decrement scores of bad actor follows.', err)) | ||
107 | } | ||
108 | } | ||
109 | |||
66 | static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) { | 110 | static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) { |
67 | const query = { | 111 | const query = { |
68 | where: { | 112 | where: { |
@@ -260,7 +304,37 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
260 | } | 304 | } |
261 | } | 305 | } |
262 | 306 | ||
263 | toFormattedJSON () { | 307 | private static incrementScores (inboxUrls: string[], value: number, t: Sequelize.Transaction) { |
308 | const inboxUrlsString = inboxUrls.map(url => `'${url}'`).join(',') | ||
309 | |||
310 | const query = 'UPDATE "actorFollow" SET "score" = "score" +' + value + ' ' + | ||
311 | 'WHERE id IN (' + | ||
312 | 'SELECT "actorFollow"."id" FROM "actorFollow" ' + | ||
313 | 'INNER JOIN "actor" ON "actor"."id" = "actorFollow"."actorId" ' + | ||
314 | 'WHERE "actor"."inboxUrl" IN (' + inboxUrlsString + ') OR "actor"."sharedInboxUrl" IN (' + inboxUrlsString + ')' + | ||
315 | ')' | ||
316 | |||
317 | const options = { | ||
318 | type: Sequelize.QueryTypes.BULKUPDATE, | ||
319 | transaction: t | ||
320 | } | ||
321 | |||
322 | return ActorFollowModel.sequelize.query(query, options) | ||
323 | } | ||
324 | |||
325 | private static listBadActorFollows () { | ||
326 | const query = { | ||
327 | where: { | ||
328 | score: { | ||
329 | [Sequelize.Op.lte]: 0 | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | return ActorFollowModel.findAll(query) | ||
335 | } | ||
336 | |||
337 | toFormattedJSON (): AccountFollow { | ||
264 | const follower = this.ActorFollower.toFormattedJSON() | 338 | const follower = this.ActorFollower.toFormattedJSON() |
265 | const following = this.ActorFollowing.toFormattedJSON() | 339 | const following = this.ActorFollowing.toFormattedJSON() |
266 | 340 | ||
@@ -268,6 +342,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
268 | id: this.id, | 342 | id: this.id, |
269 | follower, | 343 | follower, |
270 | following, | 344 | following, |
345 | score: this.score, | ||
271 | state: this.state, | 346 | state: this.state, |
272 | createdAt: this.createdAt, | 347 | createdAt: this.createdAt, |
273 | updatedAt: this.updatedAt | 348 | updatedAt: this.updatedAt |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index b88e06b41..912d8d748 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -204,7 +204,7 @@ export class ActorModel extends Model<ActorModel> { | |||
204 | VideoChannel: VideoChannelModel | 204 | VideoChannel: VideoChannelModel |
205 | 205 | ||
206 | static load (id: number) { | 206 | static load (id: number) { |
207 | return ActorModel.scope(ScopeNames.FULL).findById(id) | 207 | return ActorModel.unscoped().findById(id) |
208 | } | 208 | } |
209 | 209 | ||
210 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { | 210 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { |
@@ -267,20 +267,17 @@ export class ActorModel extends Model<ActorModel> { | |||
267 | avatar = this.Avatar.toFormattedJSON() | 267 | avatar = this.Avatar.toFormattedJSON() |
268 | } | 268 | } |
269 | 269 | ||
270 | let score: number | ||
271 | if (this.Server) { | ||
272 | score = this.Server.score | ||
273 | } | ||
274 | |||
275 | return { | 270 | return { |
276 | id: this.id, | 271 | id: this.id, |
277 | url: this.url, | 272 | url: this.url, |
278 | uuid: this.uuid, | 273 | uuid: this.uuid, |
274 | name: this.preferredUsername, | ||
279 | host: this.getHost(), | 275 | host: this.getHost(), |
280 | score, | ||
281 | followingCount: this.followingCount, | 276 | followingCount: this.followingCount, |
282 | followersCount: this.followersCount, | 277 | followersCount: this.followersCount, |
283 | avatar | 278 | avatar, |
279 | createdAt: this.createdAt, | ||
280 | updatedAt: this.updatedAt | ||
284 | } | 281 | } |
285 | } | 282 | } |
286 | 283 | ||
diff --git a/server/models/server/server.ts b/server/models/server/server.ts index d35aa0ca4..c43146156 100644 --- a/server/models/server/server.ts +++ b/server/models/server/server.ts | |||
@@ -1,8 +1,5 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AllowNull, Column, CreatedAt, Default, Is, IsInt, Max, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
3 | import { isHostValid } from '../../helpers/custom-validators/servers' | 2 | import { isHostValid } from '../../helpers/custom-validators/servers' |
4 | import { logger } from '../../helpers/logger' | ||
5 | import { SERVERS_SCORE } from '../../initializers' | ||
6 | import { throwIfNotValid } from '../utils' | 3 | import { throwIfNotValid } from '../utils' |
7 | 4 | ||
8 | @Table({ | 5 | @Table({ |
@@ -11,9 +8,6 @@ import { throwIfNotValid } from '../utils' | |||
11 | { | 8 | { |
12 | fields: [ 'host' ], | 9 | fields: [ 'host' ], |
13 | unique: true | 10 | unique: true |
14 | }, | ||
15 | { | ||
16 | fields: [ 'score' ] | ||
17 | } | 11 | } |
18 | ] | 12 | ] |
19 | }) | 13 | }) |
@@ -24,86 +18,9 @@ export class ServerModel extends Model<ServerModel> { | |||
24 | @Column | 18 | @Column |
25 | host: string | 19 | host: string |
26 | 20 | ||
27 | @AllowNull(false) | ||
28 | @Default(SERVERS_SCORE.BASE) | ||
29 | @IsInt | ||
30 | @Max(SERVERS_SCORE.MAX) | ||
31 | @Column | ||
32 | score: number | ||
33 | |||
34 | @CreatedAt | 21 | @CreatedAt |
35 | createdAt: Date | 22 | createdAt: Date |
36 | 23 | ||
37 | @UpdatedAt | 24 | @UpdatedAt |
38 | updatedAt: Date | 25 | updatedAt: Date |
39 | |||
40 | static updateServersScoreAndRemoveBadOnes (goodServers: number[], badServers: number[]) { | ||
41 | logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length) | ||
42 | |||
43 | if (goodServers.length !== 0) { | ||
44 | ServerModel.incrementScores(goodServers, SERVERS_SCORE.BONUS) | ||
45 | .catch(err => { | ||
46 | logger.error('Cannot increment scores of good servers.', err) | ||
47 | }) | ||
48 | } | ||
49 | |||
50 | if (badServers.length !== 0) { | ||
51 | ServerModel.incrementScores(badServers, SERVERS_SCORE.PENALTY) | ||
52 | .then(() => ServerModel.removeBadServers()) | ||
53 | .catch(err => { | ||
54 | if (err) logger.error('Cannot decrement scores of bad servers.', err) | ||
55 | }) | ||
56 | |||
57 | } | ||
58 | } | ||
59 | |||
60 | // Remove servers with a score of 0 (too many requests where they were unreachable) | ||
61 | private static async removeBadServers () { | ||
62 | try { | ||
63 | const servers = await ServerModel.listBadServers() | ||
64 | |||
65 | const serversRemovePromises = servers.map(server => server.destroy()) | ||
66 | await Promise.all(serversRemovePromises) | ||
67 | |||
68 | const numberOfServersRemoved = servers.length | ||
69 | |||
70 | if (numberOfServersRemoved) { | ||
71 | logger.info('Removed %d servers.', numberOfServersRemoved) | ||
72 | } else { | ||
73 | logger.info('No need to remove bad servers.') | ||
74 | } | ||
75 | } catch (err) { | ||
76 | logger.error('Cannot remove bad servers.', err) | ||
77 | } | ||
78 | } | ||
79 | |||
80 | private static incrementScores (ids: number[], value: number) { | ||
81 | const update = { | ||
82 | score: Sequelize.literal('score +' + value) | ||
83 | } | ||
84 | |||
85 | const options = { | ||
86 | where: { | ||
87 | id: { | ||
88 | [Sequelize.Op.in]: ids | ||
89 | } | ||
90 | }, | ||
91 | // In this case score is a literal and not an integer so we do not validate it | ||
92 | validate: false | ||
93 | } | ||
94 | |||
95 | return ServerModel.update(update, options) | ||
96 | } | ||
97 | |||
98 | private static listBadServers () { | ||
99 | const query = { | ||
100 | where: { | ||
101 | score: { | ||
102 | [Sequelize.Op.lte]: 0 | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | return ServerModel.findAll(query) | ||
108 | } | ||
109 | } | 26 | } |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index acc2486b3..e2cbf0422 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -228,7 +228,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
228 | const actor = this.Actor.toFormattedJSON() | 228 | const actor = this.Actor.toFormattedJSON() |
229 | const account = { | 229 | const account = { |
230 | id: this.id, | 230 | id: this.id, |
231 | name: this.name, | 231 | displayName: this.name, |
232 | description: this.description, | 232 | description: this.description, |
233 | isLocal: this.Actor.isOwned(), | 233 | isLocal: this.Actor.isOwned(), |
234 | createdAt: this.createdAt, | 234 | createdAt: this.createdAt, |