diff options
Diffstat (limited to 'server/models/account/account-follow.ts')
-rw-r--r-- | server/models/account/account-follow.ts | 130 |
1 files changed, 126 insertions, 4 deletions
diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index 7c129ab9d..6d7592326 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts | |||
@@ -1,12 +1,16 @@ | |||
1 | import { values } from 'lodash' | 1 | import { values } from 'lodash' |
2 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | 3 | ||
4 | import { addMethodsToModel } from '../utils' | 4 | import { addMethodsToModel, getSort } from '../utils' |
5 | import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' | 5 | import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' |
6 | import { FOLLOW_STATES } from '../../initializers/constants' | 6 | import { FOLLOW_STATES } from '../../initializers/constants' |
7 | 7 | ||
8 | let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> | 8 | let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> |
9 | let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget | 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 | ||
10 | 14 | ||
11 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 15 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { |
12 | AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', | 16 | AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', |
@@ -34,7 +38,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
34 | 38 | ||
35 | const classMethods = [ | 39 | const classMethods = [ |
36 | associate, | 40 | associate, |
37 | loadByAccountAndTarget | 41 | loadByAccountAndTarget, |
42 | listFollowingForApi, | ||
43 | listFollowersForApi, | ||
44 | listAcceptedFollowerUrlsForApi, | ||
45 | listAcceptedFollowingUrlsForApi | ||
38 | ] | 46 | ] |
39 | addMethodsToModel(AccountFollow, classMethods) | 47 | addMethodsToModel(AccountFollow, classMethods) |
40 | 48 | ||
@@ -49,7 +57,7 @@ function associate (models) { | |||
49 | name: 'accountId', | 57 | name: 'accountId', |
50 | allowNull: false | 58 | allowNull: false |
51 | }, | 59 | }, |
52 | as: 'accountFollowers', | 60 | as: 'AccountFollower', |
53 | onDelete: 'CASCADE' | 61 | onDelete: 'CASCADE' |
54 | }) | 62 | }) |
55 | 63 | ||
@@ -58,7 +66,7 @@ function associate (models) { | |||
58 | name: 'targetAccountId', | 66 | name: 'targetAccountId', |
59 | allowNull: false | 67 | allowNull: false |
60 | }, | 68 | }, |
61 | as: 'accountFollowing', | 69 | as: 'AccountFollowing', |
62 | onDelete: 'CASCADE' | 70 | onDelete: 'CASCADE' |
63 | }) | 71 | }) |
64 | } | 72 | } |
@@ -73,3 +81,117 @@ loadByAccountAndTarget = function (accountId: number, targetAccountId: number) { | |||
73 | 81 | ||
74 | return AccountFollow.findOne(query) | 82 | return AccountFollow.findOne(query) |
75 | } | 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.Pod ] | ||
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.Pod ] | ||
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 | } | ||