diff options
Diffstat (limited to 'server/models/account')
-rw-r--r-- | server/models/account/account-follow.ts | 228 | ||||
-rw-r--r-- | server/models/account/account.ts | 111 | ||||
-rw-r--r-- | server/models/account/user.ts | 23 |
3 files changed, 38 insertions, 324 deletions
diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts deleted file mode 100644 index 975e7ee7d..000000000 --- a/server/models/account/account-follow.ts +++ /dev/null | |||
@@ -1,228 +0,0 @@ | |||
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/accounts' | ||
6 | import { FOLLOW_STATES } from '../../initializers/constants' | ||
7 | import { ServerModel } from '../server/server' | ||
8 | import { getSort } from '../utils' | ||
9 | import { AccountModel } from './account' | ||
10 | |||
11 | @Table({ | ||
12 | tableName: 'accountFollow', | ||
13 | indexes: [ | ||
14 | { | ||
15 | fields: [ 'accountId' ] | ||
16 | }, | ||
17 | { | ||
18 | fields: [ 'targetAccountId' ] | ||
19 | }, | ||
20 | { | ||
21 | fields: [ 'accountId', 'targetAccountId' ], | ||
22 | unique: true | ||
23 | } | ||
24 | ] | ||
25 | }) | ||
26 | export class AccountFollowModel extends Model<AccountFollowModel> { | ||
27 | |||
28 | @AllowNull(false) | ||
29 | @Column(DataType.ENUM(values(FOLLOW_STATES))) | ||
30 | state: FollowState | ||
31 | |||
32 | @CreatedAt | ||
33 | createdAt: Date | ||
34 | |||
35 | @UpdatedAt | ||
36 | updatedAt: Date | ||
37 | |||
38 | @ForeignKey(() => AccountModel) | ||
39 | @Column | ||
40 | accountId: number | ||
41 | |||
42 | @BelongsTo(() => AccountModel, { | ||
43 | foreignKey: { | ||
44 | name: 'accountId', | ||
45 | allowNull: false | ||
46 | }, | ||
47 | as: 'AccountFollower', | ||
48 | onDelete: 'CASCADE' | ||
49 | }) | ||
50 | AccountFollower: AccountModel | ||
51 | |||
52 | @ForeignKey(() => AccountModel) | ||
53 | @Column | ||
54 | targetAccountId: number | ||
55 | |||
56 | @BelongsTo(() => AccountModel, { | ||
57 | foreignKey: { | ||
58 | name: 'targetAccountId', | ||
59 | allowNull: false | ||
60 | }, | ||
61 | as: 'AccountFollowing', | ||
62 | onDelete: 'CASCADE' | ||
63 | }) | ||
64 | AccountFollowing: AccountModel | ||
65 | |||
66 | static loadByAccountAndTarget (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) { | ||
67 | const query = { | ||
68 | where: { | ||
69 | accountId, | ||
70 | targetAccountId | ||
71 | }, | ||
72 | include: [ | ||
73 | { | ||
74 | model: AccountModel, | ||
75 | required: true, | ||
76 | as: 'AccountFollower' | ||
77 | }, | ||
78 | { | ||
79 | model: AccountModel, | ||
80 | required: true, | ||
81 | as: 'AccountFollowing' | ||
82 | } | ||
83 | ], | ||
84 | transaction: t | ||
85 | } | ||
86 | |||
87 | return AccountFollowModel.findOne(query) | ||
88 | } | ||
89 | |||
90 | static listFollowingForApi (id: number, start: number, count: number, sort: string) { | ||
91 | const query = { | ||
92 | distinct: true, | ||
93 | offset: start, | ||
94 | limit: count, | ||
95 | order: [ getSort(sort) ], | ||
96 | include: [ | ||
97 | { | ||
98 | model: AccountModel, | ||
99 | required: true, | ||
100 | as: 'AccountFollower', | ||
101 | where: { | ||
102 | id | ||
103 | } | ||
104 | }, | ||
105 | { | ||
106 | model: AccountModel, | ||
107 | as: 'AccountFollowing', | ||
108 | required: true, | ||
109 | include: [ ServerModel ] | ||
110 | } | ||
111 | ] | ||
112 | } | ||
113 | |||
114 | return AccountFollowModel.findAndCountAll(query) | ||
115 | .then(({ rows, count }) => { | ||
116 | return { | ||
117 | data: rows, | ||
118 | total: count | ||
119 | } | ||
120 | }) | ||
121 | } | ||
122 | |||
123 | static listFollowersForApi (id: number, start: number, count: number, sort: string) { | ||
124 | const query = { | ||
125 | distinct: true, | ||
126 | offset: start, | ||
127 | limit: count, | ||
128 | order: [ getSort(sort) ], | ||
129 | include: [ | ||
130 | { | ||
131 | model: AccountModel, | ||
132 | required: true, | ||
133 | as: 'AccountFollower', | ||
134 | include: [ ServerModel ] | ||
135 | }, | ||
136 | { | ||
137 | model: AccountModel, | ||
138 | as: 'AccountFollowing', | ||
139 | required: true, | ||
140 | where: { | ||
141 | id | ||
142 | } | ||
143 | } | ||
144 | ] | ||
145 | } | ||
146 | |||
147 | return AccountFollowModel.findAndCountAll(query) | ||
148 | .then(({ rows, count }) => { | ||
149 | return { | ||
150 | data: rows, | ||
151 | total: count | ||
152 | } | ||
153 | }) | ||
154 | } | ||
155 | |||
156 | static listAcceptedFollowerUrlsForApi (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { | ||
157 | return AccountFollowModel.createListAcceptedFollowForApiQuery('followers', accountIds, t, start, count) | ||
158 | } | ||
159 | |||
160 | static listAcceptedFollowerSharedInboxUrls (accountIds: number[], t: Sequelize.Transaction) { | ||
161 | return AccountFollowModel.createListAcceptedFollowForApiQuery('followers', accountIds, t, undefined, undefined, 'sharedInboxUrl') | ||
162 | } | ||
163 | |||
164 | static listAcceptedFollowingUrlsForApi (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) { | ||
165 | return AccountFollowModel.createListAcceptedFollowForApiQuery('following', accountIds, t, start, count) | ||
166 | } | ||
167 | |||
168 | private static async createListAcceptedFollowForApiQuery (type: 'followers' | 'following', | ||
169 | accountIds: number[], | ||
170 | t: Sequelize.Transaction, | ||
171 | start?: number, | ||
172 | count?: number, | ||
173 | columnUrl = 'url') { | ||
174 | let firstJoin: string | ||
175 | let secondJoin: string | ||
176 | |||
177 | if (type === 'followers') { | ||
178 | firstJoin = 'targetAccountId' | ||
179 | secondJoin = 'accountId' | ||
180 | } else { | ||
181 | firstJoin = 'accountId' | ||
182 | secondJoin = 'targetAccountId' | ||
183 | } | ||
184 | |||
185 | const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ] | ||
186 | const tasks: Bluebird<any>[] = [] | ||
187 | |||
188 | for (const selection of selections) { | ||
189 | let query = 'SELECT ' + selection + ' FROM "account" ' + | ||
190 | 'INNER JOIN "accountFollow" ON "accountFollow"."' + firstJoin + '" = "account"."id" ' + | ||
191 | 'INNER JOIN "account" AS "Follows" ON "accountFollow"."' + secondJoin + '" = "Follows"."id" ' + | ||
192 | 'WHERE "account"."id" = ANY ($accountIds) AND "accountFollow"."state" = \'accepted\' ' | ||
193 | |||
194 | if (count !== undefined) query += 'LIMIT ' + count | ||
195 | if (start !== undefined) query += ' OFFSET ' + start | ||
196 | |||
197 | const options = { | ||
198 | bind: { accountIds }, | ||
199 | type: Sequelize.QueryTypes.SELECT, | ||
200 | transaction: t | ||
201 | } | ||
202 | tasks.push(AccountFollowModel.sequelize.query(query, options)) | ||
203 | } | ||
204 | |||
205 | const [ followers, [ { total } ] ] = await | ||
206 | Promise.all(tasks) | ||
207 | const urls: string[] = followers.map(f => f.url) | ||
208 | |||
209 | return { | ||
210 | data: urls, | ||
211 | total: parseInt(total, 10) | ||
212 | } | ||
213 | } | ||
214 | |||
215 | toFormattedJSON () { | ||
216 | const follower = this.AccountFollower.toFormattedJSON() | ||
217 | const following = this.AccountFollowing.toFormattedJSON() | ||
218 | |||
219 | return { | ||
220 | id: this.id, | ||
221 | follower, | ||
222 | following, | ||
223 | state: this.state, | ||
224 | createdAt: this.createdAt, | ||
225 | updatedAt: this.updatedAt | ||
226 | } | ||
227 | } | ||
228 | } | ||
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index b26395fd4..1ee232537 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -5,18 +5,16 @@ import { | |||
5 | BelongsTo, | 5 | BelongsTo, |
6 | Column, | 6 | Column, |
7 | CreatedAt, | 7 | CreatedAt, |
8 | DataType, | 8 | DefaultScope, |
9 | Default, | ||
10 | ForeignKey, | 9 | ForeignKey, |
11 | HasMany, | 10 | HasMany, |
12 | Is, | 11 | Is, |
13 | IsUUID, | ||
14 | Model, | 12 | Model, |
15 | Table, | 13 | Table, |
16 | UpdatedAt | 14 | UpdatedAt |
17 | } from 'sequelize-typescript' | 15 | } from 'sequelize-typescript' |
18 | import { isUserUsernameValid } from '../../helpers/custom-validators/users' | 16 | import { isUserUsernameValid } from '../../helpers/custom-validators/users' |
19 | import { sendDeleteAccount } from '../../lib/activitypub/send' | 17 | import { sendDeleteActor } from '../../lib/activitypub/send' |
20 | import { ActorModel } from '../activitypub/actor' | 18 | import { ActorModel } from '../activitypub/actor' |
21 | import { ApplicationModel } from '../application/application' | 19 | import { ApplicationModel } from '../application/application' |
22 | import { ServerModel } from '../server/server' | 20 | import { ServerModel } from '../server/server' |
@@ -24,31 +22,30 @@ import { throwIfNotValid } from '../utils' | |||
24 | import { VideoChannelModel } from '../video/video-channel' | 22 | import { VideoChannelModel } from '../video/video-channel' |
25 | import { UserModel } from './user' | 23 | import { UserModel } from './user' |
26 | 24 | ||
27 | @Table({ | 25 | @DefaultScope({ |
28 | tableName: 'account', | 26 | include: [ |
29 | indexes: [ | ||
30 | { | ||
31 | fields: [ 'name' ] | ||
32 | }, | ||
33 | { | ||
34 | fields: [ 'serverId' ] | ||
35 | }, | ||
36 | { | ||
37 | fields: [ 'userId' ], | ||
38 | unique: true | ||
39 | }, | ||
40 | { | ||
41 | fields: [ 'applicationId' ], | ||
42 | unique: true | ||
43 | }, | ||
44 | { | 27 | { |
45 | fields: [ 'name', 'serverId', 'applicationId' ], | 28 | model: () => ActorModel, |
46 | unique: true | 29 | required: true, |
30 | include: [ | ||
31 | { | ||
32 | model: () => ServerModel, | ||
33 | required: false | ||
34 | } | ||
35 | ] | ||
47 | } | 36 | } |
48 | ] | 37 | ] |
49 | }) | 38 | }) |
39 | @Table({ | ||
40 | tableName: 'account' | ||
41 | }) | ||
50 | export class AccountModel extends Model<AccountModel> { | 42 | export class AccountModel extends Model<AccountModel> { |
51 | 43 | ||
44 | @AllowNull(false) | ||
45 | @Is('AccountName', value => throwIfNotValid(value, isUserUsernameValid, 'account name')) | ||
46 | @Column | ||
47 | name: string | ||
48 | |||
52 | @CreatedAt | 49 | @CreatedAt |
53 | createdAt: Date | 50 | createdAt: Date |
54 | 51 | ||
@@ -89,7 +86,7 @@ export class AccountModel extends Model<AccountModel> { | |||
89 | }, | 86 | }, |
90 | onDelete: 'cascade' | 87 | onDelete: 'cascade' |
91 | }) | 88 | }) |
92 | Application: ApplicationModel | 89 | Account: ApplicationModel |
93 | 90 | ||
94 | @HasMany(() => VideoChannelModel, { | 91 | @HasMany(() => VideoChannelModel, { |
95 | foreignKey: { | 92 | foreignKey: { |
@@ -103,32 +100,27 @@ export class AccountModel extends Model<AccountModel> { | |||
103 | @AfterDestroy | 100 | @AfterDestroy |
104 | static sendDeleteIfOwned (instance: AccountModel) { | 101 | static sendDeleteIfOwned (instance: AccountModel) { |
105 | if (instance.isOwned()) { | 102 | if (instance.isOwned()) { |
106 | return sendDeleteAccount(instance, undefined) | 103 | return sendDeleteActor(instance.Actor, undefined) |
107 | } | 104 | } |
108 | 105 | ||
109 | return undefined | 106 | return undefined |
110 | } | 107 | } |
111 | 108 | ||
112 | static loadApplication () { | ||
113 | return AccountModel.findOne({ | ||
114 | include: [ | ||
115 | { | ||
116 | model: ApplicationModel, | ||
117 | required: true | ||
118 | } | ||
119 | ] | ||
120 | }) | ||
121 | } | ||
122 | |||
123 | static load (id: number) { | 109 | static load (id: number) { |
124 | return AccountModel.findById(id) | 110 | return AccountModel.findById(id) |
125 | } | 111 | } |
126 | 112 | ||
127 | static loadByUUID (uuid: string) { | 113 | static loadByUUID (uuid: string) { |
128 | const query = { | 114 | const query = { |
129 | where: { | 115 | include: [ |
130 | uuid | 116 | { |
131 | } | 117 | model: ActorModel, |
118 | required: true, | ||
119 | where: { | ||
120 | uuid | ||
121 | } | ||
122 | } | ||
123 | ] | ||
132 | } | 124 | } |
133 | 125 | ||
134 | return AccountModel.findOne(query) | 126 | return AccountModel.findOne(query) |
@@ -156,25 +148,6 @@ export class AccountModel extends Model<AccountModel> { | |||
156 | return AccountModel.findOne(query) | 148 | return AccountModel.findOne(query) |
157 | } | 149 | } |
158 | 150 | ||
159 | static loadByNameAndHost (name: string, host: string) { | ||
160 | const query = { | ||
161 | where: { | ||
162 | name | ||
163 | }, | ||
164 | include: [ | ||
165 | { | ||
166 | model: ServerModel, | ||
167 | required: true, | ||
168 | where: { | ||
169 | host | ||
170 | } | ||
171 | } | ||
172 | ] | ||
173 | } | ||
174 | |||
175 | return AccountModel.findOne(query) | ||
176 | } | ||
177 | |||
178 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { | 151 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { |
179 | const query = { | 152 | const query = { |
180 | include: [ | 153 | include: [ |
@@ -192,29 +165,11 @@ export class AccountModel extends Model<AccountModel> { | |||
192 | return AccountModel.findOne(query) | 165 | return AccountModel.findOne(query) |
193 | } | 166 | } |
194 | 167 | ||
195 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { | ||
196 | const query = { | ||
197 | include: [ | ||
198 | { | ||
199 | model: ActorModel, | ||
200 | required: true, | ||
201 | where: { | ||
202 | followersUrl: { | ||
203 | [ Sequelize.Op.in ]: followersUrls | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | ], | ||
208 | transaction | ||
209 | } | ||
210 | |||
211 | return AccountModel.findAll(query) | ||
212 | } | ||
213 | |||
214 | toFormattedJSON () { | 168 | toFormattedJSON () { |
215 | const actor = this.Actor.toFormattedJSON() | 169 | const actor = this.Actor.toFormattedJSON() |
216 | const account = { | 170 | const account = { |
217 | id: this.id, | 171 | id: this.id, |
172 | name: this.name, | ||
218 | createdAt: this.createdAt, | 173 | createdAt: this.createdAt, |
219 | updatedAt: this.updatedAt | 174 | updatedAt: this.updatedAt |
220 | } | 175 | } |
@@ -223,7 +178,7 @@ export class AccountModel extends Model<AccountModel> { | |||
223 | } | 178 | } |
224 | 179 | ||
225 | toActivityPubObject () { | 180 | toActivityPubObject () { |
226 | return this.Actor.toActivityPubObject(this.name, this.uuid, 'Account') | 181 | return this.Actor.toActivityPubObject(this.name, 'Account') |
227 | } | 182 | } |
228 | 183 | ||
229 | isOwned () { | 184 | isOwned () { |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 70ed61e07..1d5759ea3 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -1,26 +1,13 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { | 2 | import { |
3 | AllowNull, | 3 | AllowNull, BeforeCreate, BeforeUpdate, Column, CreatedAt, DataType, Default, DefaultScope, HasMany, HasOne, Is, IsEmail, Model, |
4 | BeforeCreate, | 4 | Scopes, Table, UpdatedAt |
5 | BeforeUpdate, | ||
6 | Column, CreatedAt, | ||
7 | DataType, | ||
8 | Default, DefaultScope, | ||
9 | HasMany, | ||
10 | HasOne, | ||
11 | Is, | ||
12 | IsEmail, | ||
13 | Model, Scopes, | ||
14 | Table, UpdatedAt | ||
15 | } from 'sequelize-typescript' | 5 | } from 'sequelize-typescript' |
16 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' | 6 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' |
7 | import { comparePassword, cryptPassword } from '../../helpers' | ||
17 | import { | 8 | import { |
18 | comparePassword, | 9 | isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, |
19 | cryptPassword | 10 | isUserVideoQuotaValid |
20 | } from '../../helpers' | ||
21 | import { | ||
22 | isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, | ||
23 | isUserVideoQuotaValid, isUserAutoPlayVideoValid | ||
24 | } from '../../helpers/custom-validators/users' | 11 | } from '../../helpers/custom-validators/users' |
25 | import { OAuthTokenModel } from '../oauth/oauth-token' | 12 | import { OAuthTokenModel } from '../oauth/oauth-token' |
26 | import { getSort, throwIfNotValid } from '../utils' | 13 | import { getSort, throwIfNotValid } from '../utils' |