]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/account/account-follow.ts
Send server announce when users upload a video
[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 }
83
84 return AccountFollow.findOne(query)
85 }
86
87 listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
88 const query = {
89 distinct: true,
90 offset: start,
91 limit: count,
92 order: [ getSort(sort) ],
93 include: [
94 {
95 model: AccountFollow[ 'sequelize' ].models.Account,
96 required: true,
97 as: 'AccountFollower',
98 where: {
99 id
100 }
101 },
102 {
103 model: AccountFollow['sequelize'].models.Account,
104 as: 'AccountFollowing',
105 required: true,
106 include: [ AccountFollow['sequelize'].models.Server ]
107 }
108 ]
109 }
110
111 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
112 return {
113 data: rows.map(r => r.AccountFollowing),
114 total: count
115 }
116 })
117 }
118
119 listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
120 const query = {
121 distinct: true,
122 offset: start,
123 limit: count,
124 order: [ getSort(sort) ],
125 include: [
126 {
127 model: AccountFollow[ 'sequelize' ].models.Account,
128 required: true,
129 as: 'AccountFollower',
130 include: [ AccountFollow['sequelize'].models.Server ]
131 },
132 {
133 model: AccountFollow['sequelize'].models.Account,
134 as: 'AccountFollowing',
135 required: true,
136 where: {
137 id
138 }
139 }
140 ]
141 }
142
143 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
144 return {
145 data: rows.map(r => r.AccountFollower),
146 total: count
147 }
148 })
149 }
150
151 listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
152 return createListAcceptedFollowForApiQuery('followers', accountIds, start, count)
153 }
154
155 listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) {
156 return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl')
157 }
158
159 listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
160 return createListAcceptedFollowForApiQuery('following', accountIds, start, count)
161 }
162
163 // ------------------------------ UTILS ------------------------------
164
165 async function createListAcceptedFollowForApiQuery (
166 type: 'followers' | 'following',
167 accountIds: number[],
168 start?: number,
169 count?: number,
170 columnUrl = 'url'
171 ) {
172 let firstJoin: string
173 let secondJoin: string
174
175 if (type === 'followers') {
176 firstJoin = 'targetAccountId'
177 secondJoin = 'accountId'
178 } else {
179 firstJoin = 'accountId'
180 secondJoin = 'targetAccountId'
181 }
182
183 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
184 const tasks: Promise<any>[] = []
185
186 for (const selection of selections) {
187 let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
188 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
189 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
190 'WHERE "Accounts"."id" IN ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
191
192 if (start !== undefined) query += 'LIMIT ' + start
193 if (count !== undefined) query += ', ' + count
194
195 const options = {
196 bind: { accountIds: accountIds.join(',') },
197 type: Sequelize.QueryTypes.SELECT
198 }
199 tasks.push(AccountFollow['sequelize'].query(query, options))
200 }
201
202 const [ followers, [ { total } ]] = await Promise.all(tasks)
203 const urls: string[] = followers.map(f => f.url)
204
205 return {
206 data: urls,
207 total: parseInt(total, 10)
208 }
209 }