]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/users.ts
Add ability to update a video channel
[github/Chocobozzz/PeerTube.git] / server / controllers / api / users.ts
CommitLineData
4d4e5cd4 1import * as express from 'express'
ac81d1a0 2import 'multer'
c5911fd3
C
3import { extname, join } from 'path'
4import * as uuidv4 from 'uuid/v4'
490b595a 5import * as RateLimit from 'express-rate-limit'
571389d4 6import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
da854ddd 7import { retryTransactionWrapper } from '../../helpers/database-utils'
ac81d1a0 8import { processImage } from '../../helpers/image-utils'
da854ddd 9import { logger } from '../../helpers/logger'
0626e7af 10import { getFormattedObjects } from '../../helpers/utils'
490b595a 11import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers'
a5625b41 12import { updateActorAvatarInstance } from '../../lib/activitypub'
2422c46b 13import { sendUpdateActor } from '../../lib/activitypub/send'
ecb4e35f 14import { Emailer } from '../../lib/emailer'
ecb4e35f 15import { Redis } from '../../lib/redis'
50d6de9c 16import { createUserAccountAndChannel } from '../../lib/user'
65fcc311 17import {
f076daa7
C
18 asyncMiddleware,
19 authenticate,
20 ensureUserHasRight,
21 ensureUserRegistrationAllowed,
22 paginationValidator,
23 setDefaultPagination,
24 setDefaultSort,
25 token,
26 usersAddValidator,
27 usersGetValidator,
28 usersRegisterValidator,
29 usersRemoveValidator,
30 usersSortValidator,
31 usersUpdateMeValidator,
32 usersUpdateValidator,
33 usersVideoRatingValidator
65fcc311 34} from '../../middlewares'
ecb4e35f 35import {
f076daa7
C
36 usersAskResetPasswordValidator,
37 usersResetPasswordValidator,
38 usersUpdateMyAvatarValidator,
ecb4e35f
C
39 videosSortValidator
40} from '../../middlewares/validators'
3fd3ab2d
C
41import { AccountVideoRateModel } from '../../models/account/account-video-rate'
42import { UserModel } from '../../models/account/user'
f8b8c36b 43import { OAuthTokenModel } from '../../models/oauth/oauth-token'
3fd3ab2d 44import { VideoModel } from '../../models/video/video'
0883b324 45import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
0626e7af 46import { createReqFiles } from '../../helpers/express-utils'
65fcc311 47
ac81d1a0 48const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
490b595a
C
49const loginRateLimiter = new RateLimit({
50 windowMs: RATES_LIMIT.LOGIN.WINDOW_MS,
51 max: RATES_LIMIT.LOGIN.MAX,
52 delayMs: 0
53})
c5911fd3 54
65fcc311
C
55const usersRouter = express.Router()
56
57usersRouter.get('/me',
58 authenticate,
eb080476 59 asyncMiddleware(getUserInformation)
d38b8281
C
60)
61
ce5496d6
C
62usersRouter.get('/me/video-quota-used',
63 authenticate,
64 asyncMiddleware(getUserVideoQuotaUsed)
65)
66
fd45e8f4
C
67usersRouter.get('/me/videos',
68 authenticate,
69 paginationValidator,
70 videosSortValidator,
1174a847 71 setDefaultSort,
f05a1c30 72 setDefaultPagination,
fd45e8f4
C
73 asyncMiddleware(getUserVideos)
74)
75
65fcc311
C
76usersRouter.get('/me/videos/:videoId/rating',
77 authenticate,
a2431b7d 78 asyncMiddleware(usersVideoRatingValidator),
eb080476 79 asyncMiddleware(getUserVideoRating)
d38b8281 80)
9bd26629 81
65fcc311 82usersRouter.get('/',
86d13ec2
C
83 authenticate,
84 ensureUserHasRight(UserRight.MANAGE_USERS),
65fcc311
C
85 paginationValidator,
86 usersSortValidator,
1174a847 87 setDefaultSort,
f05a1c30 88 setDefaultPagination,
eb080476 89 asyncMiddleware(listUsers)
5c39adb7
C
90)
91
8094a898 92usersRouter.get('/:id',
94ff4c23
C
93 authenticate,
94 ensureUserHasRight(UserRight.MANAGE_USERS),
a2431b7d 95 asyncMiddleware(usersGetValidator),
8094a898
C
96 getUser
97)
98
65fcc311
C
99usersRouter.post('/',
100 authenticate,
954605a8 101 ensureUserHasRight(UserRight.MANAGE_USERS),
a2431b7d
C
102 asyncMiddleware(usersAddValidator),
103 asyncMiddleware(createUserRetryWrapper)
9bd26629
C
104)
105
65fcc311 106usersRouter.post('/register',
a2431b7d
C
107 asyncMiddleware(ensureUserRegistrationAllowed),
108 asyncMiddleware(usersRegisterValidator),
47e0652b 109 asyncMiddleware(registerUserRetryWrapper)
2c2e9092
C
110)
111
8094a898
C
112usersRouter.put('/me',
113 authenticate,
114 usersUpdateMeValidator,
eb080476 115 asyncMiddleware(updateMe)
8094a898
C
116)
117
c5911fd3
C
118usersRouter.post('/me/avatar/pick',
119 authenticate,
120 reqAvatarFile,
121 usersUpdateMyAvatarValidator,
122 asyncMiddleware(updateMyAvatar)
123)
124
65fcc311
C
125usersRouter.put('/:id',
126 authenticate,
954605a8 127 ensureUserHasRight(UserRight.MANAGE_USERS),
a2431b7d 128 asyncMiddleware(usersUpdateValidator),
eb080476 129 asyncMiddleware(updateUser)
9bd26629
C
130)
131
65fcc311
C
132usersRouter.delete('/:id',
133 authenticate,
954605a8 134 ensureUserHasRight(UserRight.MANAGE_USERS),
a2431b7d 135 asyncMiddleware(usersRemoveValidator),
eb080476 136 asyncMiddleware(removeUser)
9bd26629 137)
6606150c 138
ecb4e35f
C
139usersRouter.post('/ask-reset-password',
140 asyncMiddleware(usersAskResetPasswordValidator),
141 asyncMiddleware(askResetUserPassword)
142)
143
144usersRouter.post('/:id/reset-password',
145 asyncMiddleware(usersResetPasswordValidator),
146 asyncMiddleware(resetUserPassword)
147)
148
490b595a
C
149usersRouter.post('/token',
150 loginRateLimiter,
151 token,
152 success
153)
9bd26629 154// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route
9457bf88
C
155
156// ---------------------------------------------------------------------------
157
65fcc311
C
158export {
159 usersRouter
160}
9457bf88
C
161
162// ---------------------------------------------------------------------------
163
fd45e8f4 164async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
3fd3ab2d 165 const user = res.locals.oauth.token.User as UserModel
0883b324
C
166 const resultList = await VideoModel.listAccountVideosForApi(
167 user.Account.id,
168 req.query.start as number,
169 req.query.count as number,
170 req.query.sort as VideoSortField,
171 false // Display my NSFW videos
172 )
fd45e8f4
C
173
174 return res.json(getFormattedObjects(resultList.data, resultList.total))
175}
176
eb080476 177async function createUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
72c7248b 178 const options = {
47e0652b 179 arguments: [ req ],
72c7248b
C
180 errorMessage: 'Cannot insert the user with many retries.'
181 }
182
f05a1c30 183 const { user, account } = await retryTransactionWrapper(createUser, options)
eb080476 184
f05a1c30
C
185 return res.json({
186 user: {
187 id: user.id,
6b738c7a
C
188 account: {
189 id: account.id,
190 uuid: account.Actor.uuid
191 }
f05a1c30
C
192 }
193 }).end()
72c7248b
C
194}
195
47e0652b 196async function createUser (req: express.Request) {
4771e000 197 const body: UserCreate = req.body
f05a1c30 198 const userToCreate = new UserModel({
4771e000
C
199 username: body.username,
200 password: body.password,
201 email: body.email,
0883b324 202 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
7efe153b 203 autoPlayVideo: true,
954605a8 204 role: body.role,
b0f9f39e 205 videoQuota: body.videoQuota
9bd26629
C
206 })
207
f05a1c30 208 const { user, account } = await createUserAccountAndChannel(userToCreate)
eb080476 209
38fa2065 210 logger.info('User %s with its channel and account created.', body.username)
f05a1c30
C
211
212 return { user, account }
9bd26629
C
213}
214
47e0652b
C
215async function registerUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
216 const options = {
217 arguments: [ req ],
218 errorMessage: 'Cannot insert the user with many retries.'
219 }
220
221 await retryTransactionWrapper(registerUser, options)
222
223 return res.type('json').status(204).end()
224}
225
226async function registerUser (req: express.Request) {
77a5501f
C
227 const body: UserCreate = req.body
228
3fd3ab2d 229 const user = new UserModel({
77a5501f
C
230 username: body.username,
231 password: body.password,
232 email: body.email,
0883b324 233 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
7efe153b 234 autoPlayVideo: true,
954605a8 235 role: UserRole.USER,
77a5501f
C
236 videoQuota: CONFIG.USER.VIDEO_QUOTA
237 })
238
38fa2065 239 await createUserAccountAndChannel(user)
47e0652b
C
240
241 logger.info('User %s with its channel and account registered.', body.username)
77a5501f
C
242}
243
eb080476 244async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
fd45e8f4 245 // We did not load channels in res.locals.user
3fd3ab2d 246 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
eb080476
C
247
248 return res.json(user.toFormattedJSON())
99a64bfe
C
249}
250
ce5496d6
C
251async function getUserVideoQuotaUsed (req: express.Request, res: express.Response, next: express.NextFunction) {
252 // We did not load channels in res.locals.user
253 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
254 const videoQuotaUsed = await UserModel.getOriginalVideoFileTotalFromUser(user)
255
256 return res.json({
257 videoQuotaUsed
258 })
259}
260
8094a898 261function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
ce5496d6 262 return res.json((res.locals.user as UserModel).toFormattedJSON())
8094a898
C
263}
264
eb080476 265async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
0a6658fd 266 const videoId = +req.params.videoId
571389d4 267 const accountId = +res.locals.oauth.token.User.Account.id
d38b8281 268
3fd3ab2d 269 const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
faab3a84
C
270 const rating = ratingObj ? ratingObj.type : 'none'
271
272 const json: FormattedUserVideoRate = {
273 videoId,
274 rating
275 }
276 res.json(json)
d38b8281
C
277}
278
eb080476 279async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
3fd3ab2d 280 const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort)
eb080476
C
281
282 return res.json(getFormattedObjects(resultList.data, resultList.total))
9bd26629
C
283}
284
eb080476 285async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
3fd3ab2d 286 const user = await UserModel.loadById(req.params.id)
eb080476
C
287
288 await user.destroy()
289
290 return res.sendStatus(204)
9bd26629
C
291}
292
eb080476 293async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) {
8094a898 294 const body: UserUpdateMe = req.body
4771e000 295
2422c46b 296 const user: UserModel = res.locals.oauth.token.user
1d49e1e2 297
eb080476
C
298 if (body.password !== undefined) user.password = body.password
299 if (body.email !== undefined) user.email = body.email
0883b324 300 if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy
7efe153b 301 if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
eb080476 302
2422c46b
C
303 await sequelizeTypescript.transaction(async t => {
304 await user.save({ transaction: t })
305
ed56ad11 306 if (body.displayName !== undefined) user.Account.name = body.displayName
2422c46b
C
307 if (body.description !== undefined) user.Account.description = body.description
308 await user.Account.save({ transaction: t })
309
310 await sendUpdateActor(user.Account, t)
311 })
eb080476 312
d412e80e 313 return res.sendStatus(204)
9bd26629
C
314}
315
c5911fd3
C
316async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) {
317 const avatarPhysicalFile = req.files['avatarfile'][0]
265ba139
C
318 const user = res.locals.oauth.token.user
319 const actor = user.Account.Actor
c5911fd3 320
c5911fd3
C
321 const extension = extname(avatarPhysicalFile.filename)
322 const avatarName = uuidv4() + extension
ac81d1a0
C
323 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
324 await processImage(avatarPhysicalFile, destination, AVATARS_SIZE)
c5911fd3 325
a5625b41 326 const avatar = await sequelizeTypescript.transaction(async t => {
bb82394c
C
327 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
328 await updatedActor.save({ transaction: t })
c5911fd3 329
2422c46b 330 await sendUpdateActor(user.Account, t)
c5911fd3 331
bb82394c 332 return updatedActor.Avatar
c5911fd3
C
333 })
334
335 return res
336 .json({
337 avatar: avatar.toFormattedJSON()
338 })
339 .end()
340}
341
eb080476 342async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
8094a898 343 const body: UserUpdate = req.body
3fd3ab2d 344 const user = res.locals.user as UserModel
f8b8c36b 345 const roleChanged = body.role !== undefined && body.role !== user.role
8094a898
C
346
347 if (body.email !== undefined) user.email = body.email
348 if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota
954605a8 349 if (body.role !== undefined) user.role = body.role
8094a898 350
eb080476
C
351 await user.save()
352
f8b8c36b
C
353 // Destroy user token to refresh rights
354 if (roleChanged) {
355 await OAuthTokenModel.deleteUserToken(user.id)
356 }
357
265ba139
C
358 // Don't need to send this update to followers, these attributes are not propagated
359
eb080476 360 return res.sendStatus(204)
8094a898
C
361}
362
ecb4e35f
C
363async function askResetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) {
364 const user = res.locals.user as UserModel
365
366 const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id)
367 const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
368 await Emailer.Instance.addForgetPasswordEmailJob(user.email, url)
369
370 return res.status(204).end()
371}
372
373async function resetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) {
374 const user = res.locals.user as UserModel
375 user.password = req.body.password
376
377 await user.save()
378
379 return res.status(204).end()
380}
381
69818c93 382function success (req: express.Request, res: express.Response, next: express.NextFunction) {
9457bf88
C
383 res.end()
384}