]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/user/user.ts
Client: handle multiple file resolutions
[github/Chocobozzz/PeerTube.git] / server / models / user / user.ts
CommitLineData
65fcc311 1import { values } from 'lodash'
e02643f3 2import * as Sequelize from 'sequelize'
b0f9f39e 3import * as Promise from 'bluebird'
65fcc311 4
74889a71
C
5import { getSort } from '../utils'
6import { USER_ROLES } from '../../initializers'
65fcc311
C
7import {
8 cryptPassword,
9 comparePassword,
10 isUserPasswordValid,
11 isUserUsernameValid,
b0f9f39e
C
12 isUserDisplayNSFWValid,
13 isUserVideoQuotaValid
74889a71 14} from '../../helpers'
40298b02 15import { VideoResolution } from '../../../shared'
9bd26629 16
74889a71 17import { addMethodsToModel } from '../utils'
e02643f3 18import {
e02643f3
C
19 UserInstance,
20 UserAttributes,
21
22 UserMethods
23} from './user-interface'
24
25let User: Sequelize.Model<UserInstance, UserAttributes>
26let isPasswordMatch: UserMethods.IsPasswordMatch
0aef76c4 27let toFormattedJSON: UserMethods.ToFormattedJSON
e02643f3
C
28let isAdmin: UserMethods.IsAdmin
29let countTotal: UserMethods.CountTotal
30let getByUsername: UserMethods.GetByUsername
31let list: UserMethods.List
32let listForApi: UserMethods.ListForApi
33let loadById: UserMethods.LoadById
34let loadByUsername: UserMethods.LoadByUsername
35let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
b0f9f39e 36let isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo
e02643f3 37
127944aa
C
38export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
39 User = sequelize.define<UserInstance, UserAttributes>('User',
feb4bdfd
C
40 {
41 password: {
67bf9b96
C
42 type: DataTypes.STRING,
43 allowNull: false,
44 validate: {
075f16ca 45 passwordValid: value => {
65fcc311 46 const res = isUserPasswordValid(value)
67bf9b96
C
47 if (res === false) throw new Error('Password not valid.')
48 }
49 }
feb4bdfd
C
50 },
51 username: {
67bf9b96
C
52 type: DataTypes.STRING,
53 allowNull: false,
54 validate: {
075f16ca 55 usernameValid: value => {
65fcc311 56 const res = isUserUsernameValid(value)
67bf9b96
C
57 if (res === false) throw new Error('Username not valid.')
58 }
59 }
feb4bdfd 60 },
ad4a8a1c 61 email: {
5804c0db 62 type: DataTypes.STRING(400),
ad4a8a1c
C
63 allowNull: false,
64 validate: {
65 isEmail: true
66 }
67 },
1d49e1e2
C
68 displayNSFW: {
69 type: DataTypes.BOOLEAN,
70 allowNull: false,
71 defaultValue: false,
72 validate: {
075f16ca 73 nsfwValid: value => {
65fcc311 74 const res = isUserDisplayNSFWValid(value)
1d49e1e2
C
75 if (res === false) throw new Error('Display NSFW is not valid.')
76 }
77 }
78 },
feb4bdfd 79 role: {
65fcc311 80 type: DataTypes.ENUM(values(USER_ROLES)),
67bf9b96 81 allowNull: false
b0f9f39e
C
82 },
83 videoQuota: {
84 type: DataTypes.BIGINT,
85 allowNull: false,
86 validate: {
87 videoQuotaValid: value => {
88 const res = isUserVideoQuotaValid(value)
89 if (res === false) throw new Error('Video quota is not valid.')
90 }
91 }
feb4bdfd
C
92 }
93 },
94 {
319d072e
C
95 indexes: [
96 {
5d67f289
C
97 fields: [ 'username' ],
98 unique: true
ad4a8a1c
C
99 },
100 {
101 fields: [ 'email' ],
102 unique: true
319d072e
C
103 }
104 ],
feb4bdfd
C
105 hooks: {
106 beforeCreate: beforeCreateOrUpdate,
107 beforeUpdate: beforeCreateOrUpdate
108 }
109 }
110 )
111
e02643f3
C
112 const classMethods = [
113 associate,
114
115 countTotal,
116 getByUsername,
117 list,
118 listForApi,
119 loadById,
120 loadByUsername,
121 loadByUsernameOrEmail
122 ]
123 const instanceMethods = [
124 isPasswordMatch,
0aef76c4 125 toFormattedJSON,
b0f9f39e
C
126 isAdmin,
127 isAbleToUploadVideo
e02643f3
C
128 ]
129 addMethodsToModel(User, classMethods, instanceMethods)
130
feb4bdfd 131 return User
9bd26629 132}
69b0a27c 133
69818c93 134function beforeCreateOrUpdate (user: UserInstance) {
6fcd19ba
C
135 return cryptPassword(user.password).then(hash => {
136 user.password = hash
137 return undefined
26d7d31b 138 })
feb4bdfd 139}
69b0a27c 140
26d7d31b
C
141// ------------------------------ METHODS ------------------------------
142
6fcd19ba
C
143isPasswordMatch = function (this: UserInstance, password: string) {
144 return comparePassword(password, this.password)
26d7d31b
C
145}
146
0aef76c4 147toFormattedJSON = function (this: UserInstance) {
26d7d31b 148 return {
feb4bdfd 149 id: this.id,
26d7d31b 150 username: this.username,
ad4a8a1c 151 email: this.email,
1d49e1e2 152 displayNSFW: this.displayNSFW,
d74a0680 153 role: this.role,
b0f9f39e 154 videoQuota: this.videoQuota,
feb4bdfd 155 createdAt: this.createdAt
26d7d31b
C
156 }
157}
198b205c 158
70c065d6 159isAdmin = function (this: UserInstance) {
65fcc311 160 return this.role === USER_ROLES.ADMIN
198b205c
GS
161}
162
b0f9f39e
C
163isAbleToUploadVideo = function (this: UserInstance, videoFile: Express.Multer.File) {
164 if (this.videoQuota === -1) return Promise.resolve(true)
165
166 return getOriginalVideoFileTotalFromUser(this).then(totalBytes => {
167 return (videoFile.size + totalBytes) < this.videoQuota
168 })
169}
170
26d7d31b 171// ------------------------------ STATICS ------------------------------
69b0a27c 172
feb4bdfd 173function associate (models) {
e02643f3 174 User.hasOne(models.Author, {
4712081f
C
175 foreignKey: 'userId',
176 onDelete: 'cascade'
177 })
178
e02643f3 179 User.hasMany(models.OAuthToken, {
feb4bdfd
C
180 foreignKey: 'userId',
181 onDelete: 'cascade'
182 })
183}
184
6fcd19ba
C
185countTotal = function () {
186 return this.count()
089ff2f2
C
187}
188
69818c93 189getByUsername = function (username: string) {
feb4bdfd
C
190 const query = {
191 where: {
192 username: username
193 }
194 }
195
e02643f3 196 return User.findOne(query)
9bd26629
C
197}
198
6fcd19ba
C
199list = function () {
200 return User.findAll()
00d6b0dd
C
201}
202
6fcd19ba 203listForApi = function (start: number, count: number, sort: string) {
feb4bdfd
C
204 const query = {
205 offset: start,
206 limit: count,
65fcc311 207 order: [ getSort(sort) ]
feb4bdfd
C
208 }
209
6fcd19ba
C
210 return User.findAndCountAll(query).then(({ rows, count }) => {
211 return {
212 data: rows,
213 total: count
214 }
feb4bdfd 215 })
69b0a27c
C
216}
217
6fcd19ba
C
218loadById = function (id: number) {
219 return User.findById(id)
68a3b9f2
C
220}
221
6fcd19ba 222loadByUsername = function (username: string) {
feb4bdfd
C
223 const query = {
224 where: {
556ddc31 225 username
feb4bdfd
C
226 }
227 }
228
6fcd19ba 229 return User.findOne(query)
9bd26629 230}
ad4a8a1c 231
6fcd19ba 232loadByUsernameOrEmail = function (username: string, email: string) {
ad4a8a1c
C
233 const query = {
234 where: {
235 $or: [ { username }, { email } ]
236 }
237 }
238
556ddc31
C
239 // FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387
240 return (User as any).findOne(query)
ad4a8a1c 241}
b0f9f39e
C
242
243// ---------------------------------------------------------------------------
244
245function getOriginalVideoFileTotalFromUser (user: UserInstance) {
77a5501f 246 // attributes = [] because we don't want other fields than the sum
b0f9f39e 247 const query = {
b0f9f39e 248 where: {
40298b02 249 resolution: VideoResolution.ORIGINAL
b0f9f39e
C
250 },
251 include: [
252 {
77a5501f
C
253 attributes: [],
254 model: User['sequelize'].models.Video,
b0f9f39e
C
255 include: [
256 {
77a5501f
C
257 attributes: [],
258 model: User['sequelize'].models.Author,
b0f9f39e
C
259 include: [
260 {
77a5501f
C
261 attributes: [],
262 model: User['sequelize'].models.User,
263 where: {
264 id: user.id
265 }
b0f9f39e
C
266 }
267 ]
268 }
269 ]
270 }
271 ]
272 }
273
77a5501f 274 return User['sequelize'].models.VideoFile.sum('size', query)
b0f9f39e 275}