]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/account/account-follow.ts
Federate likes/dislikes
[github/Chocobozzz/PeerTube.git] / server / models / account / account-follow.ts
CommitLineData
7a7724e6 1import { values } from 'lodash'
e4f97bab
C
2import * as Sequelize from 'sequelize'
3
51548b31 4import { addMethodsToModel, getSort } from '../utils'
7a7724e6
C
5import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface'
6import { FOLLOW_STATES } from '../../initializers/constants'
e4f97bab
C
7
8let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes>
7a7724e6 9let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
51548b31
C
10let listFollowingForApi: AccountFollowMethods.ListFollowingForApi
11let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
12let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
13let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
efc32059 14let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
7e9334c3 15let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
e4f97bab
C
16
17export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
18 AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
7a7724e6
C
19 {
20 state: {
21 type: DataTypes.ENUM(values(FOLLOW_STATES)),
22 allowNull: false
23 }
24 },
e4f97bab
C
25 {
26 indexes: [
27 {
350e31d6
C
28 fields: [ 'accountId' ]
29 },
30 {
31 fields: [ 'targetAccountId' ]
e4f97bab
C
32 },
33 {
350e31d6 34 fields: [ 'accountId', 'targetAccountId' ],
e4f97bab
C
35 unique: true
36 }
37 ]
38 }
39 )
40
41 const classMethods = [
350e31d6 42 associate,
51548b31
C
43 loadByAccountAndTarget,
44 listFollowingForApi,
45 listFollowersForApi,
46 listAcceptedFollowerUrlsForApi,
efc32059
C
47 listAcceptedFollowingUrlsForApi,
48 listAcceptedFollowerSharedInboxUrls
e4f97bab 49 ]
7e9334c3
C
50 const instanceMethods = [
51 toFormattedJSON
52 ]
53 addMethodsToModel(AccountFollow, classMethods, instanceMethods)
e4f97bab
C
54
55 return AccountFollow
56}
57
58// ------------------------------ STATICS ------------------------------
59
60function associate (models) {
61 AccountFollow.belongsTo(models.Account, {
62 foreignKey: {
63 name: 'accountId',
64 allowNull: false
65 },
51548b31 66 as: 'AccountFollower',
e4f97bab
C
67 onDelete: 'CASCADE'
68 })
69
70 AccountFollow.belongsTo(models.Account, {
71 foreignKey: {
72 name: 'targetAccountId',
73 allowNull: false
74 },
51548b31 75 as: 'AccountFollowing',
e4f97bab
C
76 onDelete: 'CASCADE'
77 })
78}
7a7724e6 79
7e9334c3
C
80toFormattedJSON = function (this: AccountFollowInstance) {
81 const follower = this.AccountFollower.toFormattedJSON()
82 const following = this.AccountFollowing.toFormattedJSON()
83
84 const json = {
85 id: this.id,
86 follower,
87 following,
88 state: this.state,
89 createdAt: this.createdAt,
90 updatedAt: this.updatedAt
91 }
92
93 return json
94}
95
0032ebe9 96loadByAccountAndTarget = function (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) {
7a7724e6
C
97 const query = {
98 where: {
99 accountId,
100 targetAccountId
54141398
C
101 },
102 include: [
103 {
104 model: AccountFollow[ 'sequelize' ].models.Account,
105 required: true,
106 as: 'AccountFollower'
107 },
108 {
109 model: AccountFollow['sequelize'].models.Account,
110 required: true,
111 as: 'AccountFollowing'
112 }
0032ebe9
C
113 ],
114 transaction: t
7a7724e6
C
115 }
116
117 return AccountFollow.findOne(query)
118}
51548b31
C
119
120listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
121 const query = {
122 distinct: true,
123 offset: start,
124 limit: count,
125 order: [ getSort(sort) ],
126 include: [
127 {
128 model: AccountFollow[ 'sequelize' ].models.Account,
129 required: true,
130 as: 'AccountFollower',
131 where: {
132 id
133 }
134 },
135 {
136 model: AccountFollow['sequelize'].models.Account,
137 as: 'AccountFollowing',
138 required: true,
60862425 139 include: [ AccountFollow['sequelize'].models.Server ]
51548b31
C
140 }
141 ]
142 }
143
144 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
145 return {
7e9334c3 146 data: rows,
51548b31
C
147 total: count
148 }
149 })
150}
151
152listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
153 const query = {
154 distinct: true,
155 offset: start,
156 limit: count,
157 order: [ getSort(sort) ],
158 include: [
159 {
160 model: AccountFollow[ 'sequelize' ].models.Account,
161 required: true,
162 as: 'AccountFollower',
60862425 163 include: [ AccountFollow['sequelize'].models.Server ]
51548b31
C
164 },
165 {
166 model: AccountFollow['sequelize'].models.Account,
167 as: 'AccountFollowing',
168 required: true,
169 where: {
170 id
171 }
172 }
173 ]
174 }
175
176 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
177 return {
7e9334c3 178 data: rows,
51548b31
C
179 total: count
180 }
181 })
182}
183
efc32059
C
184listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
185 return createListAcceptedFollowForApiQuery('followers', accountIds, start, count)
51548b31
C
186}
187
efc32059
C
188listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) {
189 return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl')
190}
191
192listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
193 return createListAcceptedFollowForApiQuery('following', accountIds, start, count)
51548b31
C
194}
195
196// ------------------------------ UTILS ------------------------------
197
efc32059
C
198async function createListAcceptedFollowForApiQuery (
199 type: 'followers' | 'following',
200 accountIds: number[],
201 start?: number,
202 count?: number,
203 columnUrl = 'url'
204) {
51548b31
C
205 let firstJoin: string
206 let secondJoin: string
207
208 if (type === 'followers') {
209 firstJoin = 'targetAccountId'
210 secondJoin = 'accountId'
211 } else {
212 firstJoin = 'accountId'
213 secondJoin = 'targetAccountId'
214 }
215
efc32059 216 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
51548b31
C
217 const tasks: Promise<any>[] = []
218
219 for (const selection of selections) {
8e10cf1a
C
220 let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
221 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
222 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
d7d5611c 223 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
51548b31 224
e71bcc0f
C
225 if (count !== undefined) query += 'LIMIT ' + count
226 if (start !== undefined) query += ' OFFSET ' + start
51548b31
C
227
228 const options = {
d7d5611c 229 bind: { accountIds },
51548b31
C
230 type: Sequelize.QueryTypes.SELECT
231 }
232 tasks.push(AccountFollow['sequelize'].query(query, options))
233 }
234
235 const [ followers, [ { total } ]] = await Promise.all(tasks)
236 const urls: string[] = followers.map(f => f.url)
237
238 return {
239 data: urls,
240 total: parseInt(total, 10)
241 }
242}