X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Faccount%2Faccount-follow.ts;h=724f37baa6b6038815611ab1d8f5cf652f70e8b0;hb=25ed141c7c7631ef21d8764c1163fbf8a6591391;hp=9bf03b253150180c6c7dc0999ca4cd204e6c3bd4;hpb=e4f97babf701481b55cc10fb3448feab5f97c867;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index 9bf03b253..724f37baa 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts @@ -1,26 +1,37 @@ +import { values } from 'lodash' import * as Sequelize from 'sequelize' -import { addMethodsToModel } from '../utils' -import { - AccountFollowInstance, - AccountFollowAttributes, - - AccountFollowMethods -} from './account-follow-interface' +import { addMethodsToModel, getSort } from '../utils' +import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' +import { FOLLOW_STATES } from '../../initializers/constants' let AccountFollow: Sequelize.Model +let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget +let listFollowingForApi: AccountFollowMethods.ListFollowingForApi +let listFollowersForApi: AccountFollowMethods.ListFollowersForApi +let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi +let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi +let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls +let toFormattedJSON: AccountFollowMethods.ToFormattedJSON export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { AccountFollow = sequelize.define('AccountFollow', - { }, + { + state: { + type: DataTypes.ENUM(values(FOLLOW_STATES)), + allowNull: false + } + }, { indexes: [ { - fields: [ 'accountId' ], - unique: true + fields: [ 'accountId' ] + }, + { + fields: [ 'targetAccountId' ] }, { - fields: [ 'targetAccountId' ], + fields: [ 'accountId', 'targetAccountId' ], unique: true } ] @@ -28,9 +39,18 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da ) const classMethods = [ - associate + associate, + loadByAccountAndTarget, + listFollowingForApi, + listFollowersForApi, + listAcceptedFollowerUrlsForApi, + listAcceptedFollowingUrlsForApi, + listAcceptedFollowerSharedInboxUrls ] - addMethodsToModel(AccountFollow, classMethods) + const instanceMethods = [ + toFormattedJSON + ] + addMethodsToModel(AccountFollow, classMethods, instanceMethods) return AccountFollow } @@ -43,6 +63,7 @@ function associate (models) { name: 'accountId', allowNull: false }, + as: 'AccountFollower', onDelete: 'CASCADE' }) @@ -51,6 +72,173 @@ function associate (models) { name: 'targetAccountId', allowNull: false }, + as: 'AccountFollowing', onDelete: 'CASCADE' }) } + +toFormattedJSON = function (this: AccountFollowInstance) { + const follower = this.AccountFollower.toFormattedJSON() + const following = this.AccountFollowing.toFormattedJSON() + + const json = { + id: this.id, + follower, + following, + state: this.state, + createdAt: this.createdAt, + updatedAt: this.updatedAt + } + + return json +} + +loadByAccountAndTarget = function (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) { + const query = { + where: { + accountId, + targetAccountId + }, + include: [ + { + model: AccountFollow[ 'sequelize' ].models.Account, + required: true, + as: 'AccountFollower' + }, + { + model: AccountFollow['sequelize'].models.Account, + required: true, + as: 'AccountFollowing' + } + ], + transaction: t + } + + return AccountFollow.findOne(query) +} + +listFollowingForApi = function (id: number, start: number, count: number, sort: string) { + const query = { + distinct: true, + offset: start, + limit: count, + order: [ getSort(sort) ], + include: [ + { + model: AccountFollow[ 'sequelize' ].models.Account, + required: true, + as: 'AccountFollower', + where: { + id + } + }, + { + model: AccountFollow['sequelize'].models.Account, + as: 'AccountFollowing', + required: true, + include: [ AccountFollow['sequelize'].models.Server ] + } + ] + } + + return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { + return { + data: rows, + total: count + } + }) +} + +listFollowersForApi = function (id: number, start: number, count: number, sort: string) { + const query = { + distinct: true, + offset: start, + limit: count, + order: [ getSort(sort) ], + include: [ + { + model: AccountFollow[ 'sequelize' ].models.Account, + required: true, + as: 'AccountFollower', + include: [ AccountFollow['sequelize'].models.Server ] + }, + { + model: AccountFollow['sequelize'].models.Account, + as: 'AccountFollowing', + required: true, + where: { + id + } + } + ] + } + + return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { + return { + data: rows, + total: count + } + }) +} + +listAcceptedFollowerUrlsForApi = function (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { + return createListAcceptedFollowForApiQuery('followers', accountIds, t, start, count) +} + +listAcceptedFollowerSharedInboxUrls = function (accountIds: number[], t: Sequelize.Transaction) { + return createListAcceptedFollowForApiQuery('followers', accountIds, t, undefined, undefined, 'sharedInboxUrl') +} + +listAcceptedFollowingUrlsForApi = function (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { + return createListAcceptedFollowForApiQuery('following', accountIds, t, start, count) +} + +// ------------------------------ UTILS ------------------------------ + +async function createListAcceptedFollowForApiQuery ( + type: 'followers' | 'following', + accountIds: number[], + t: Sequelize.Transaction, + start?: number, + count?: number, + columnUrl = 'url' +) { + let firstJoin: string + let secondJoin: string + + if (type === 'followers') { + firstJoin = 'targetAccountId' + secondJoin = 'accountId' + } else { + firstJoin = 'accountId' + secondJoin = 'targetAccountId' + } + + const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ] + const tasks: Promise[] = [] + + for (const selection of selections) { + let query = 'SELECT ' + selection + ' FROM "Accounts" ' + + 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' + + 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' + + 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' ' + + if (count !== undefined) query += 'LIMIT ' + count + if (start !== undefined) query += ' OFFSET ' + start + + const options = { + bind: { accountIds }, + type: Sequelize.QueryTypes.SELECT, + transaction: t + } + tasks.push(AccountFollow['sequelize'].query(query, options)) + } + + const [ followers, [ { total } ]] = await Promise.all(tasks) + const urls: string[] = followers.map(f => f.url) + + return { + data: urls, + total: parseInt(total, 10) + } +}