diff options
Diffstat (limited to 'server/models/user/user.ts')
-rw-r--r-- | server/models/user/user.ts | 312 |
1 files changed, 0 insertions, 312 deletions
diff --git a/server/models/user/user.ts b/server/models/user/user.ts deleted file mode 100644 index b974418d4..000000000 --- a/server/models/user/user.ts +++ /dev/null | |||
@@ -1,312 +0,0 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import * as Promise from 'bluebird' | ||
3 | |||
4 | import { getSort, addMethodsToModel } from '../utils' | ||
5 | import { | ||
6 | cryptPassword, | ||
7 | comparePassword, | ||
8 | isUserPasswordValid, | ||
9 | isUserUsernameValid, | ||
10 | isUserDisplayNSFWValid, | ||
11 | isUserVideoQuotaValid, | ||
12 | isUserRoleValid | ||
13 | } from '../../helpers' | ||
14 | import { UserRight, USER_ROLE_LABELS, hasUserRight } from '../../../shared' | ||
15 | |||
16 | import { | ||
17 | UserInstance, | ||
18 | UserAttributes, | ||
19 | |||
20 | UserMethods | ||
21 | } from './user-interface' | ||
22 | |||
23 | let User: Sequelize.Model<UserInstance, UserAttributes> | ||
24 | let isPasswordMatch: UserMethods.IsPasswordMatch | ||
25 | let hasRight: UserMethods.HasRight | ||
26 | let toFormattedJSON: UserMethods.ToFormattedJSON | ||
27 | let countTotal: UserMethods.CountTotal | ||
28 | let getByUsername: UserMethods.GetByUsername | ||
29 | let listForApi: UserMethods.ListForApi | ||
30 | let loadById: UserMethods.LoadById | ||
31 | let loadByUsername: UserMethods.LoadByUsername | ||
32 | let loadByUsernameAndPopulateChannels: UserMethods.LoadByUsernameAndPopulateChannels | ||
33 | let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail | ||
34 | let isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo | ||
35 | |||
36 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
37 | User = sequelize.define<UserInstance, UserAttributes>('User', | ||
38 | { | ||
39 | password: { | ||
40 | type: DataTypes.STRING, | ||
41 | allowNull: false, | ||
42 | validate: { | ||
43 | passwordValid: value => { | ||
44 | const res = isUserPasswordValid(value) | ||
45 | if (res === false) throw new Error('Password not valid.') | ||
46 | } | ||
47 | } | ||
48 | }, | ||
49 | username: { | ||
50 | type: DataTypes.STRING, | ||
51 | allowNull: false, | ||
52 | validate: { | ||
53 | usernameValid: value => { | ||
54 | const res = isUserUsernameValid(value) | ||
55 | if (res === false) throw new Error('Username not valid.') | ||
56 | } | ||
57 | } | ||
58 | }, | ||
59 | email: { | ||
60 | type: DataTypes.STRING(400), | ||
61 | allowNull: false, | ||
62 | validate: { | ||
63 | isEmail: true | ||
64 | } | ||
65 | }, | ||
66 | displayNSFW: { | ||
67 | type: DataTypes.BOOLEAN, | ||
68 | allowNull: false, | ||
69 | defaultValue: false, | ||
70 | validate: { | ||
71 | nsfwValid: value => { | ||
72 | const res = isUserDisplayNSFWValid(value) | ||
73 | if (res === false) throw new Error('Display NSFW is not valid.') | ||
74 | } | ||
75 | } | ||
76 | }, | ||
77 | role: { | ||
78 | type: DataTypes.INTEGER, | ||
79 | allowNull: false, | ||
80 | validate: { | ||
81 | roleValid: value => { | ||
82 | const res = isUserRoleValid(value) | ||
83 | if (res === false) throw new Error('Role is not valid.') | ||
84 | } | ||
85 | } | ||
86 | }, | ||
87 | videoQuota: { | ||
88 | type: DataTypes.BIGINT, | ||
89 | allowNull: false, | ||
90 | validate: { | ||
91 | videoQuotaValid: value => { | ||
92 | const res = isUserVideoQuotaValid(value) | ||
93 | if (res === false) throw new Error('Video quota is not valid.') | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | }, | ||
98 | { | ||
99 | indexes: [ | ||
100 | { | ||
101 | fields: [ 'username' ], | ||
102 | unique: true | ||
103 | }, | ||
104 | { | ||
105 | fields: [ 'email' ], | ||
106 | unique: true | ||
107 | } | ||
108 | ], | ||
109 | hooks: { | ||
110 | beforeCreate: beforeCreateOrUpdate, | ||
111 | beforeUpdate: beforeCreateOrUpdate | ||
112 | } | ||
113 | } | ||
114 | ) | ||
115 | |||
116 | const classMethods = [ | ||
117 | associate, | ||
118 | |||
119 | countTotal, | ||
120 | getByUsername, | ||
121 | listForApi, | ||
122 | loadById, | ||
123 | loadByUsername, | ||
124 | loadByUsernameAndPopulateChannels, | ||
125 | loadByUsernameOrEmail | ||
126 | ] | ||
127 | const instanceMethods = [ | ||
128 | hasRight, | ||
129 | isPasswordMatch, | ||
130 | toFormattedJSON, | ||
131 | isAbleToUploadVideo | ||
132 | ] | ||
133 | addMethodsToModel(User, classMethods, instanceMethods) | ||
134 | |||
135 | return User | ||
136 | } | ||
137 | |||
138 | function beforeCreateOrUpdate (user: UserInstance) { | ||
139 | if (user.changed('password')) { | ||
140 | return cryptPassword(user.password) | ||
141 | .then(hash => { | ||
142 | user.password = hash | ||
143 | return undefined | ||
144 | }) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | // ------------------------------ METHODS ------------------------------ | ||
149 | |||
150 | hasRight = function (this: UserInstance, right: UserRight) { | ||
151 | return hasUserRight(this.role, right) | ||
152 | } | ||
153 | |||
154 | isPasswordMatch = function (this: UserInstance, password: string) { | ||
155 | return comparePassword(password, this.password) | ||
156 | } | ||
157 | |||
158 | toFormattedJSON = function (this: UserInstance) { | ||
159 | const json = { | ||
160 | id: this.id, | ||
161 | username: this.username, | ||
162 | email: this.email, | ||
163 | displayNSFW: this.displayNSFW, | ||
164 | role: this.role, | ||
165 | roleLabel: USER_ROLE_LABELS[this.role], | ||
166 | videoQuota: this.videoQuota, | ||
167 | createdAt: this.createdAt, | ||
168 | author: { | ||
169 | id: this.Author.id, | ||
170 | uuid: this.Author.uuid | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (Array.isArray(this.Author.VideoChannels) === true) { | ||
175 | const videoChannels = this.Author.VideoChannels | ||
176 | .map(c => c.toFormattedJSON()) | ||
177 | .sort((v1, v2) => { | ||
178 | if (v1.createdAt < v2.createdAt) return -1 | ||
179 | if (v1.createdAt === v2.createdAt) return 0 | ||
180 | |||
181 | return 1 | ||
182 | }) | ||
183 | |||
184 | json['videoChannels'] = videoChannels | ||
185 | } | ||
186 | |||
187 | return json | ||
188 | } | ||
189 | |||
190 | isAbleToUploadVideo = function (this: UserInstance, videoFile: Express.Multer.File) { | ||
191 | if (this.videoQuota === -1) return Promise.resolve(true) | ||
192 | |||
193 | return getOriginalVideoFileTotalFromUser(this).then(totalBytes => { | ||
194 | return (videoFile.size + totalBytes) < this.videoQuota | ||
195 | }) | ||
196 | } | ||
197 | |||
198 | // ------------------------------ STATICS ------------------------------ | ||
199 | |||
200 | function associate (models) { | ||
201 | User.hasOne(models.Author, { | ||
202 | foreignKey: 'userId', | ||
203 | onDelete: 'cascade' | ||
204 | }) | ||
205 | |||
206 | User.hasMany(models.OAuthToken, { | ||
207 | foreignKey: 'userId', | ||
208 | onDelete: 'cascade' | ||
209 | }) | ||
210 | } | ||
211 | |||
212 | countTotal = function () { | ||
213 | return this.count() | ||
214 | } | ||
215 | |||
216 | getByUsername = function (username: string) { | ||
217 | const query = { | ||
218 | where: { | ||
219 | username: username | ||
220 | }, | ||
221 | include: [ { model: User['sequelize'].models.Author, required: true } ] | ||
222 | } | ||
223 | |||
224 | return User.findOne(query) | ||
225 | } | ||
226 | |||
227 | listForApi = function (start: number, count: number, sort: string) { | ||
228 | const query = { | ||
229 | offset: start, | ||
230 | limit: count, | ||
231 | order: [ getSort(sort) ], | ||
232 | include: [ { model: User['sequelize'].models.Author, required: true } ] | ||
233 | } | ||
234 | |||
235 | return User.findAndCountAll(query).then(({ rows, count }) => { | ||
236 | return { | ||
237 | data: rows, | ||
238 | total: count | ||
239 | } | ||
240 | }) | ||
241 | } | ||
242 | |||
243 | loadById = function (id: number) { | ||
244 | const options = { | ||
245 | include: [ { model: User['sequelize'].models.Author, required: true } ] | ||
246 | } | ||
247 | |||
248 | return User.findById(id, options) | ||
249 | } | ||
250 | |||
251 | loadByUsername = function (username: string) { | ||
252 | const query = { | ||
253 | where: { | ||
254 | username | ||
255 | }, | ||
256 | include: [ { model: User['sequelize'].models.Author, required: true } ] | ||
257 | } | ||
258 | |||
259 | return User.findOne(query) | ||
260 | } | ||
261 | |||
262 | loadByUsernameAndPopulateChannels = function (username: string) { | ||
263 | const query = { | ||
264 | where: { | ||
265 | username | ||
266 | }, | ||
267 | include: [ | ||
268 | { | ||
269 | model: User['sequelize'].models.Author, | ||
270 | required: true, | ||
271 | include: [ User['sequelize'].models.VideoChannel ] | ||
272 | } | ||
273 | ] | ||
274 | } | ||
275 | |||
276 | return User.findOne(query) | ||
277 | } | ||
278 | |||
279 | loadByUsernameOrEmail = function (username: string, email: string) { | ||
280 | const query = { | ||
281 | include: [ { model: User['sequelize'].models.Author, required: true } ], | ||
282 | where: { | ||
283 | [Sequelize.Op.or]: [ { username }, { email } ] | ||
284 | } | ||
285 | } | ||
286 | |||
287 | // FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387 | ||
288 | return (User as any).findOne(query) | ||
289 | } | ||
290 | |||
291 | // --------------------------------------------------------------------------- | ||
292 | |||
293 | function getOriginalVideoFileTotalFromUser (user: UserInstance) { | ||
294 | // Don't use sequelize because we need to use a sub query | ||
295 | const query = 'SELECT SUM("size") AS "total" FROM ' + | ||
296 | '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' + | ||
297 | 'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' + | ||
298 | 'INNER JOIN "VideoChannels" ON "VideoChannels"."id" = "Videos"."channelId" ' + | ||
299 | 'INNER JOIN "Authors" ON "VideoChannels"."authorId" = "Authors"."id" ' + | ||
300 | 'INNER JOIN "Users" ON "Authors"."userId" = "Users"."id" ' + | ||
301 | 'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t' | ||
302 | |||
303 | const options = { | ||
304 | bind: { userId: user.id }, | ||
305 | type: Sequelize.QueryTypes.SELECT | ||
306 | } | ||
307 | return User['sequelize'].query(query, options).then(([ { total } ]) => { | ||
308 | if (total === null) return 0 | ||
309 | |||
310 | return parseInt(total, 10) | ||
311 | }) | ||
312 | } | ||