Fix video upload and videos list
[oweals/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 (accountId: number, start?: number, count?: number) {
150   return createListAcceptedFollowForApiQuery('followers', accountId, start, count)
151 }
152
153 listAcceptedFollowingUrlsForApi = function (accountId: number, start?: number, count?: number) {
154   return createListAcceptedFollowForApiQuery('following', accountId, start, count)
155 }
156
157 // ------------------------------ UTILS ------------------------------
158
159 async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', accountId: 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 = [ '"Follows"."url" AS "url"', 'COUNT(*) AS "total"' ]
172   const tasks: Promise<any>[] = []
173
174   for (const selection of selections) {
175     let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
176       'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
177       'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
178       'WHERE "Accounts"."id" = $accountId AND "AccountFollows"."state" = \'accepted\' '
179
180     if (start !== undefined) query += 'LIMIT ' + start
181     if (count !== undefined) query += ', ' + count
182
183     const options = {
184       bind: { accountId },
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 }