]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/activitypub/actor-follow.ts
Begin moving video channel to actor
[github/Chocobozzz/PeerTube.git] / server / models / activitypub / actor-follow.ts
CommitLineData
50d6de9c
C
1import * as Bluebird from 'bluebird'
2import { values } from 'lodash'
3import * as Sequelize from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { FollowState } from '../../../shared/models/actors'
6import { FOLLOW_STATES } from '../../initializers/constants'
7import { ServerModel } from '../server/server'
8import { getSort } from '../utils'
9import { ActorModel } from './actor'
10
11@Table({
12 tableName: 'actorFollow',
13 indexes: [
14 {
15 fields: [ 'actorId' ]
16 },
17 {
18 fields: [ 'targetActorId' ]
19 },
20 {
21 fields: [ 'actorId', 'targetActorId' ],
22 unique: true
23 }
24 ]
25})
26export class ActorFollowModel extends Model<ActorFollowModel> {
27
28 @AllowNull(false)
29 @Column(DataType.ENUM(values(FOLLOW_STATES)))
30 state: FollowState
31
32 @CreatedAt
33 createdAt: Date
34
35 @UpdatedAt
36 updatedAt: Date
37
38 @ForeignKey(() => ActorModel)
39 @Column
40 actorId: number
41
42 @BelongsTo(() => ActorModel, {
43 foreignKey: {
44 name: 'actorId',
45 allowNull: false
46 },
47 as: 'ActorFollower',
48 onDelete: 'CASCADE'
49 })
50 ActorFollower: ActorModel
51
52 @ForeignKey(() => ActorModel)
53 @Column
54 targetActorId: number
55
56 @BelongsTo(() => ActorModel, {
57 foreignKey: {
58 name: 'targetActorId',
59 allowNull: false
60 },
61 as: 'ActorFollowing',
62 onDelete: 'CASCADE'
63 })
64 ActorFollowing: ActorModel
65
66 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) {
67 const query = {
68 where: {
69 actorId,
70 targetActorId: targetActorId
71 },
72 include: [
73 {
74 model: ActorModel,
75 required: true,
76 as: 'ActorFollower'
77 },
78 {
79 model: ActorModel,
80 required: true,
81 as: 'ActorFollowing'
82 }
83 ],
84 transaction: t
85 }
86
87 return ActorFollowModel.findOne(query)
88 }
89
90 static loadByActorAndTargetHost (actorId: number, targetHost: string, t?: Sequelize.Transaction) {
91 const query = {
92 where: {
93 actorId
94 },
95 include: [
96 {
97 model: ActorModel,
98 required: true,
99 as: 'ActorFollower'
100 },
101 {
102 model: ActorModel,
103 required: true,
104 as: 'ActorFollowing',
105 include: [
106 {
107 model: ServerModel,
108 required: true,
109 where: {
110 host: targetHost
111 }
112 }
113 ]
114 }
115 ],
116 transaction: t
117 }
118
119 return ActorFollowModel.findOne(query)
120 }
121
122 static listFollowingForApi (id: number, start: number, count: number, sort: string) {
123 const query = {
124 distinct: true,
125 offset: start,
126 limit: count,
127 order: [ getSort(sort) ],
128 include: [
129 {
130 model: ActorModel,
131 required: true,
132 as: 'ActorFollower',
133 where: {
134 id
135 }
136 },
137 {
138 model: ActorModel,
139 as: 'ActorFollowing',
140 required: true,
141 include: [ ServerModel ]
142 }
143 ]
144 }
145
146 return ActorFollowModel.findAndCountAll(query)
147 .then(({ rows, count }) => {
148 return {
149 data: rows,
150 total: count
151 }
152 })
153 }
154
155 static listFollowersForApi (id: number, start: number, count: number, sort: string) {
156 const query = {
157 distinct: true,
158 offset: start,
159 limit: count,
160 order: [ getSort(sort) ],
161 include: [
162 {
163 model: ActorModel,
164 required: true,
165 as: 'ActorFollower',
166 include: [ ServerModel ]
167 },
168 {
169 model: ActorModel,
170 as: 'ActorFollowing',
171 required: true,
172 where: {
173 id
174 }
175 }
176 ]
177 }
178
179 return ActorFollowModel.findAndCountAll(query)
180 .then(({ rows, count }) => {
181 return {
182 data: rows,
183 total: count
184 }
185 })
186 }
187
188 static listAcceptedFollowerUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
189 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
190 }
191
192 static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Sequelize.Transaction) {
193 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, undefined, undefined, 'sharedInboxUrl')
194 }
195
196 static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
197 return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count)
198 }
199
200 private static async createListAcceptedFollowForApiQuery (type: 'followers' | 'following',
201 actorIds: number[],
202 t: Sequelize.Transaction,
203 start?: number,
204 count?: number,
205 columnUrl = 'url') {
206 let firstJoin: string
207 let secondJoin: string
208
209 if (type === 'followers') {
210 firstJoin = 'targetActorId'
211 secondJoin = 'actorId'
212 } else {
213 firstJoin = 'actorId'
214 secondJoin = 'targetActorId'
215 }
216
217 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
218 const tasks: Bluebird<any>[] = []
219
220 for (const selection of selections) {
221 let query = 'SELECT ' + selection + ' FROM "actor" ' +
222 'INNER JOIN "actorFollow" ON "actorFollow"."' + firstJoin + '" = "actor"."id" ' +
223 'INNER JOIN "actor" AS "Follows" ON "actorFollow"."' + secondJoin + '" = "Follows"."id" ' +
224 'WHERE "actor"."id" = ANY ($actorIds) AND "actorFollow"."state" = \'accepted\' '
225
226 if (count !== undefined) query += 'LIMIT ' + count
227 if (start !== undefined) query += ' OFFSET ' + start
228
229 const options = {
230 bind: { actorIds },
231 type: Sequelize.QueryTypes.SELECT,
232 transaction: t
233 }
234 tasks.push(ActorFollowModel.sequelize.query(query, options))
235 }
236
237 const [ followers, [ { total } ] ] = await
238 Promise.all(tasks)
239 const urls: string[] = followers.map(f => f.url)
240
241 return {
242 data: urls,
243 total: parseInt(total, 10)
244 }
245 }
246
247 toFormattedJSON () {
248 const follower = this.ActorFollower.toFormattedJSON()
249 const following = this.ActorFollowing.toFormattedJSON()
250
251 return {
252 id: this.id,
253 follower,
254 following,
255 state: this.state,
256 createdAt: this.createdAt,
257 updatedAt: this.updatedAt
258 }
259 }
260}