aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-11-15 10:10:41 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-11-27 19:40:51 +0100
commit51548b31815c6f96f314ae96588a9adca150519d (patch)
treeb3298447b7ac128823016fdec92d083e07d9432e /server/models
parent350e31d6b64e4973dfa5e9f7b46841cb09aeb1ad (diff)
downloadPeerTube-51548b31815c6f96f314ae96588a9adca150519d.tar.gz
PeerTube-51548b31815c6f96f314ae96588a9adca150519d.tar.zst
PeerTube-51548b31815c6f96f314ae96588a9adca150519d.zip
Add follow tabs
Following Follow Followers
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account-follow-interface.ts16
-rw-r--r--server/models/account/account-follow.ts130
-rw-r--r--server/models/account/account-interface.ts8
-rw-r--r--server/models/account/account.ts132
4 files changed, 149 insertions, 137 deletions
diff --git a/server/models/account/account-follow-interface.ts b/server/models/account/account-follow-interface.ts
index efdff915e..413dad190 100644
--- a/server/models/account/account-follow-interface.ts
+++ b/server/models/account/account-follow-interface.ts
@@ -1,13 +1,26 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird' 2import * as Bluebird from 'bluebird'
3import { FollowState } from '../../../shared/models/accounts/follow.model' 3import { FollowState } from '../../../shared/models/accounts/follow.model'
4import { ResultList } from '../../../shared/models/result-list.model'
5import { AccountInstance } from './account-interface'
4 6
5export namespace AccountFollowMethods { 7export namespace AccountFollowMethods {
6 export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance> 8 export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance>
9
10 export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
11 export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
12
13 export type ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
14 export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
7} 15}
8 16
9export interface AccountFollowClass { 17export interface AccountFollowClass {
10 loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget 18 loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
19 listFollowersForApi: AccountFollowMethods.ListFollowersForApi
20 listFollowingForApi: AccountFollowMethods.ListFollowingForApi
21
22 listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
23 listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
11} 24}
12 25
13export interface AccountFollowAttributes { 26export interface AccountFollowAttributes {
@@ -20,6 +33,9 @@ export interface AccountFollowInstance extends AccountFollowClass, AccountFollow
20 id: number 33 id: number
21 createdAt: Date 34 createdAt: Date
22 updatedAt: Date 35 updatedAt: Date
36
37 AccountFollower?: AccountInstance
38 AccountFollowing?: AccountInstance
23} 39}
24 40
25export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {} 41export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
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 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
3 3
4import { addMethodsToModel } from '../utils' 4import { addMethodsToModel, getSort } from '../utils'
5import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' 5import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface'
6import { FOLLOW_STATES } from '../../initializers/constants' 6import { FOLLOW_STATES } from '../../initializers/constants'
7 7
8let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> 8let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes>
9let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget 9let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
10let listFollowingForApi: AccountFollowMethods.ListFollowingForApi
11let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
12let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
13let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
10 14
11export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { 15export 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
85listFollowingForApi = 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
117listFollowersForApi = 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
149listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) {
150 return createListAcceptedFollowForApiQuery('followers', id, start, count)
151}
152
153listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) {
154 return createListAcceptedFollowForApiQuery('following', id, start, count)
155}
156
157// ------------------------------ UTILS ------------------------------
158
159async 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}
diff --git a/server/models/account/account-interface.ts b/server/models/account/account-interface.ts
index 6fc36ae9d..ce1afec02 100644
--- a/server/models/account/account-interface.ts
+++ b/server/models/account/account-interface.ts
@@ -15,10 +15,6 @@ export namespace AccountMethods {
15 export type LoadLocalByName = (name: string) => Bluebird<AccountInstance> 15 export type LoadLocalByName = (name: string) => Bluebird<AccountInstance>
16 export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance> 16 export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance>
17 export type ListOwned = () => Bluebird<AccountInstance[]> 17 export type ListOwned = () => Bluebird<AccountInstance[]>
18 export type ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
19 export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList<string> >
20 export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
21 export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
22 18
23 export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor 19 export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
24 export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount 20 export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount
@@ -38,10 +34,6 @@ export interface AccountClass {
38 loadLocalByName: AccountMethods.LoadLocalByName 34 loadLocalByName: AccountMethods.LoadLocalByName
39 loadByNameAndHost: AccountMethods.LoadByNameAndHost 35 loadByNameAndHost: AccountMethods.LoadByNameAndHost
40 listOwned: AccountMethods.ListOwned 36 listOwned: AccountMethods.ListOwned
41 listAcceptedFollowerUrlsForApi: AccountMethods.ListAcceptedFollowerUrlsForApi
42 listAcceptedFollowingUrlsForApi: AccountMethods.ListAcceptedFollowingUrlsForApi
43 listFollowingForApi: AccountMethods.ListFollowingForApi
44 listFollowersForApi: AccountMethods.ListFollowersForApi
45} 37}
46 38
47export interface AccountAttributes { 39export interface AccountAttributes {
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index d2293a939..e90eaae5e 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -23,7 +23,7 @@ import {
23 AccountMethods 23 AccountMethods
24} from './account-interface' 24} from './account-interface'
25import { sendDeleteAccount } from '../../lib/activitypub/send-request' 25import { sendDeleteAccount } from '../../lib/activitypub/send-request'
26import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 26import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
27 27
28let Account: Sequelize.Model<AccountInstance, AccountAttributes> 28let Account: Sequelize.Model<AccountInstance, AccountAttributes>
29let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID 29let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID
@@ -34,10 +34,6 @@ let loadByUrl: AccountMethods.LoadByUrl
34let loadLocalByName: AccountMethods.LoadLocalByName 34let loadLocalByName: AccountMethods.LoadLocalByName
35let loadByNameAndHost: AccountMethods.LoadByNameAndHost 35let loadByNameAndHost: AccountMethods.LoadByNameAndHost
36let listOwned: AccountMethods.ListOwned 36let listOwned: AccountMethods.ListOwned
37let listAcceptedFollowerUrlsForApi: AccountMethods.ListAcceptedFollowerUrlsForApi
38let listAcceptedFollowingUrlsForApi: AccountMethods.ListAcceptedFollowingUrlsForApi
39let listFollowingForApi: AccountMethods.ListFollowingForApi
40let listFollowersForApi: AccountMethods.ListFollowersForApi
41let isOwned: AccountMethods.IsOwned 37let isOwned: AccountMethods.IsOwned
42let toActivityPubObject: AccountMethods.ToActivityPubObject 38let toActivityPubObject: AccountMethods.ToActivityPubObject
43let toFormattedJSON: AccountMethods.ToFormattedJSON 39let toFormattedJSON: AccountMethods.ToFormattedJSON
@@ -185,7 +181,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
185 unique: true 181 unique: true
186 }, 182 },
187 { 183 {
188 fields: [ 'name', 'podId' ], 184 fields: [ 'name', 'podId', 'applicationId' ],
189 unique: true 185 unique: true
190 } 186 }
191 ], 187 ],
@@ -202,11 +198,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
202 loadByUrl, 198 loadByUrl,
203 loadLocalByName, 199 loadLocalByName,
204 loadByNameAndHost, 200 loadByNameAndHost,
205 listOwned, 201 listOwned
206 listAcceptedFollowerUrlsForApi,
207 listAcceptedFollowingUrlsForApi,
208 listFollowingForApi,
209 listFollowersForApi
210 ] 202 ]
211 const instanceMethods = [ 203 const instanceMethods = [
212 isOwned, 204 isOwned,
@@ -286,9 +278,11 @@ function afterDestroy (account: AccountInstance) {
286} 278}
287 279
288toFormattedJSON = function (this: AccountInstance) { 280toFormattedJSON = function (this: AccountInstance) {
281 let host = this.Pod ? this.Pod.host : CONFIG.WEBSERVER.HOST
282
289 const json = { 283 const json = {
290 id: this.id, 284 id: this.id,
291 host: this.Pod.host, 285 host,
292 name: this.name 286 name: this.name
293 } 287 }
294 288
@@ -346,7 +340,7 @@ getFollowerSharedInboxUrls = function (this: AccountInstance) {
346} 340}
347 341
348getFollowingUrl = function (this: AccountInstance) { 342getFollowingUrl = function (this: AccountInstance) {
349 return this.url + '/followers' 343 return this.url + '/following'
350} 344}
351 345
352getFollowersUrl = function (this: AccountInstance) { 346getFollowersUrl = function (this: AccountInstance) {
@@ -369,76 +363,6 @@ listOwned = function () {
369 return Account.findAll(query) 363 return Account.findAll(query)
370} 364}
371 365
372listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) {
373 return createListAcceptedFollowForApiQuery('followers', id, start, count)
374}
375
376listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) {
377 return createListAcceptedFollowForApiQuery('following', id, start, count)
378}
379
380listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
381 const query = {
382 distinct: true,
383 offset: start,
384 limit: count,
385 order: [ getSort(sort) ],
386 include: [
387 {
388 model: Account['sequelize'].models.AccountFollow,
389 required: true,
390 as: 'following',
391 include: [
392 {
393 model: Account['sequelize'].models.Account,
394 as: 'accountFollowing',
395 required: true,
396 include: [ Account['sequelize'].models.Pod ]
397 }
398 ]
399 }
400 ]
401 }
402
403 return Account.findAndCountAll(query).then(({ rows, count }) => {
404 return {
405 data: rows,
406 total: count
407 }
408 })
409}
410
411listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
412 const query = {
413 distinct: true,
414 offset: start,
415 limit: count,
416 order: [ getSort(sort) ],
417 include: [
418 {
419 model: Account['sequelize'].models.AccountFollow,
420 required: true,
421 as: 'followers',
422 include: [
423 {
424 model: Account['sequelize'].models.Account,
425 as: 'accountFollowers',
426 required: true,
427 include: [ Account['sequelize'].models.Pod ]
428 }
429 ]
430 }
431 ]
432 }
433
434 return Account.findAndCountAll(query).then(({ rows, count }) => {
435 return {
436 data: rows,
437 total: count
438 }
439 })
440}
441
442loadApplication = function () { 366loadApplication = function () {
443 return Account.findOne({ 367 return Account.findOne({
444 include: [ 368 include: [
@@ -527,45 +451,3 @@ loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Se
527 451
528 return Account.find(query) 452 return Account.find(query)
529} 453}
530
531// ------------------------------ UTILS ------------------------------
532
533async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) {
534 let firstJoin: string
535 let secondJoin: string
536
537 if (type === 'followers') {
538 firstJoin = 'targetAccountId'
539 secondJoin = 'accountId'
540 } else {
541 firstJoin = 'accountId'
542 secondJoin = 'targetAccountId'
543 }
544
545 const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ]
546 const tasks: Promise<any>[] = []
547
548 for (const selection of selections) {
549 let query = 'SELECT ' + selection + ' FROM "Account" ' +
550 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' +
551 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' +
552 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' +
553 'LIMIT ' + start
554
555 if (count !== undefined) query += ', ' + count
556
557 const options = {
558 bind: { id },
559 type: Sequelize.QueryTypes.SELECT
560 }
561 tasks.push(Account['sequelize'].query(query, options))
562 }
563
564 const [ followers, [ { total } ]] = await Promise.all(tasks)
565 const urls: string[] = followers.map(f => f.url)
566
567 return {
568 data: urls,
569 total: parseInt(total, 10)
570 }
571}