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