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