1 import * as Bluebird from 'bluebird'
2 import { values } from 'lodash'
3 import * as Sequelize from 'sequelize'
4 import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
5 import { FollowState } from '../../../shared/models/actors'
6 import { FOLLOW_STATES } from '../../initializers/constants'
7 import { ServerModel } from '../server/server'
8 import { getSort } from '../utils'
9 import { ActorModel } from './actor'
12 tableName: 'actorFollow',
18 fields: [ 'targetActorId' ]
21 fields: [ 'actorId', 'targetActorId' ],
26 export class ActorFollowModel extends Model<ActorFollowModel> {
29 @Column(DataType.ENUM(values(FOLLOW_STATES)))
38 @ForeignKey(() => ActorModel)
42 @BelongsTo(() => ActorModel, {
50 ActorFollower: ActorModel
52 @ForeignKey(() => ActorModel)
56 @BelongsTo(() => ActorModel, {
58 name: 'targetActorId',
64 ActorFollowing: ActorModel
66 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) {
70 targetActorId: targetActorId
87 return ActorFollowModel.findOne(query)
90 static loadByActorAndTargetHost (actorId: number, targetHost: string, t?: Sequelize.Transaction) {
104 as: 'ActorFollowing',
119 return ActorFollowModel.findOne(query)
122 static listFollowingForApi (id: number, start: number, count: number, sort: string) {
127 order: [ getSort(sort) ],
139 as: 'ActorFollowing',
141 include: [ ServerModel ]
146 return ActorFollowModel.findAndCountAll(query)
147 .then(({ rows, count }) => {
155 static listFollowersForApi (id: number, start: number, count: number, sort: string) {
160 order: [ getSort(sort) ],
166 include: [ ServerModel ]
170 as: 'ActorFollowing',
179 return ActorFollowModel.findAndCountAll(query)
180 .then(({ rows, count }) => {
188 static listAcceptedFollowerUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
189 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
192 static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Sequelize.Transaction) {
193 return ActorFollowModel.createListAcceptedFollowForApiQuery(
204 static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
205 return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count)
208 private static async createListAcceptedFollowForApiQuery (
209 type: 'followers' | 'following',
211 t: Sequelize.Transaction,
217 let firstJoin: string
218 let secondJoin: string
220 if (type === 'followers') {
221 firstJoin = 'targetActorId'
222 secondJoin = 'actorId'
224 firstJoin = 'actorId'
225 secondJoin = 'targetActorId'
228 const selections: string[] = []
229 if (distinct === true) selections.push('DISTINCT("Follows"."' + columnUrl + '") AS "url"')
230 else selections.push('"Follows"."' + columnUrl + '" AS "url"')
232 selections.push('COUNT(*) AS "total"')
234 const tasks: Bluebird<any>[] = []
236 for (let selection of selections) {
237 let query = 'SELECT ' + selection + ' FROM "actor" ' +
238 'INNER JOIN "actorFollow" ON "actorFollow"."' + firstJoin + '" = "actor"."id" ' +
239 'INNER JOIN "actor" AS "Follows" ON "actorFollow"."' + secondJoin + '" = "Follows"."id" ' +
240 'WHERE "actor"."id" = ANY ($actorIds) AND "actorFollow"."state" = \'accepted\' '
242 if (count !== undefined) query += 'LIMIT ' + count
243 if (start !== undefined) query += ' OFFSET ' + start
247 type: Sequelize.QueryTypes.SELECT,
250 tasks.push(ActorFollowModel.sequelize.query(query, options))
253 const [ followers, [ { total } ] ] = await
255 const urls: string[] = followers.map(f => f.url)
259 total: parseInt(total, 10)
264 const follower = this.ActorFollower.toFormattedJSON()
265 const following = this.ActorFollowing.toFormattedJSON()
272 createdAt: this.createdAt,
273 updatedAt: this.updatedAt