diff options
author | Chocobozzz <me@florianbigard.com> | 2018-08-16 11:26:22 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-08-27 09:41:54 +0200 |
commit | d03cd8bb206efcaa3fa6899ce82f5b1838a9f46f (patch) | |
tree | 8e4a4cbfce580ab5b49ac903a2abae436c7f7168 /server/controllers/api/users.ts | |
parent | ef65dcf5eaa01f13602b4c783c5af1db61ab6c44 (diff) | |
download | PeerTube-d03cd8bb206efcaa3fa6899ce82f5b1838a9f46f.tar.gz PeerTube-d03cd8bb206efcaa3fa6899ce82f5b1838a9f46f.tar.zst PeerTube-d03cd8bb206efcaa3fa6899ce82f5b1838a9f46f.zip |
Split users controller
Diffstat (limited to 'server/controllers/api/users.ts')
-rw-r--r-- | server/controllers/api/users.ts | 477 |
1 files changed, 0 insertions, 477 deletions
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts deleted file mode 100644 index 543b20baa..000000000 --- a/server/controllers/api/users.ts +++ /dev/null | |||
@@ -1,477 +0,0 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'multer' | ||
3 | import * as RateLimit from 'express-rate-limit' | ||
4 | import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared' | ||
5 | import { logger } from '../../helpers/logger' | ||
6 | import { getFormattedObjects } from '../../helpers/utils' | ||
7 | import { CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers' | ||
8 | import { sendUpdateActor } from '../../lib/activitypub/send' | ||
9 | import { Emailer } from '../../lib/emailer' | ||
10 | import { Redis } from '../../lib/redis' | ||
11 | import { createUserAccountAndChannel } from '../../lib/user' | ||
12 | import { | ||
13 | asyncMiddleware, | ||
14 | asyncRetryTransactionMiddleware, | ||
15 | authenticate, | ||
16 | ensureUserHasRight, | ||
17 | ensureUserRegistrationAllowed, | ||
18 | ensureUserRegistrationAllowedForIP, | ||
19 | paginationValidator, | ||
20 | setDefaultPagination, | ||
21 | setDefaultSort, | ||
22 | token, | ||
23 | usersAddValidator, | ||
24 | usersGetValidator, | ||
25 | usersRegisterValidator, | ||
26 | usersRemoveValidator, | ||
27 | usersSortValidator, | ||
28 | usersUpdateMeValidator, | ||
29 | usersUpdateValidator, | ||
30 | usersVideoRatingValidator | ||
31 | } from '../../middlewares' | ||
32 | import { | ||
33 | deleteMeValidator, | ||
34 | usersAskResetPasswordValidator, | ||
35 | usersBlockingValidator, | ||
36 | usersResetPasswordValidator, | ||
37 | videoImportsSortValidator, | ||
38 | videosSortValidator | ||
39 | } from '../../middlewares/validators' | ||
40 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | ||
41 | import { UserModel } from '../../models/account/user' | ||
42 | import { OAuthTokenModel } from '../../models/oauth/oauth-token' | ||
43 | import { VideoModel } from '../../models/video/video' | ||
44 | import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type' | ||
45 | import { createReqFiles } from '../../helpers/express-utils' | ||
46 | import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.model' | ||
47 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' | ||
48 | import { updateActorAvatarFile } from '../../lib/avatar' | ||
49 | import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger' | ||
50 | import { VideoImportModel } from '../../models/video/video-import' | ||
51 | |||
52 | const auditLogger = auditLoggerFactory('users') | ||
53 | |||
54 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) | ||
55 | const loginRateLimiter = new RateLimit({ | ||
56 | windowMs: RATES_LIMIT.LOGIN.WINDOW_MS, | ||
57 | max: RATES_LIMIT.LOGIN.MAX, | ||
58 | delayMs: 0 | ||
59 | }) | ||
60 | |||
61 | const usersRouter = express.Router() | ||
62 | |||
63 | usersRouter.get('/me', | ||
64 | authenticate, | ||
65 | asyncMiddleware(getUserInformation) | ||
66 | ) | ||
67 | usersRouter.delete('/me', | ||
68 | authenticate, | ||
69 | asyncMiddleware(deleteMeValidator), | ||
70 | asyncMiddleware(deleteMe) | ||
71 | ) | ||
72 | |||
73 | usersRouter.get('/me/video-quota-used', | ||
74 | authenticate, | ||
75 | asyncMiddleware(getUserVideoQuotaUsed) | ||
76 | ) | ||
77 | |||
78 | usersRouter.get('/me/videos/imports', | ||
79 | authenticate, | ||
80 | paginationValidator, | ||
81 | videoImportsSortValidator, | ||
82 | setDefaultSort, | ||
83 | setDefaultPagination, | ||
84 | asyncMiddleware(getUserVideoImports) | ||
85 | ) | ||
86 | |||
87 | usersRouter.get('/me/videos', | ||
88 | authenticate, | ||
89 | paginationValidator, | ||
90 | videosSortValidator, | ||
91 | setDefaultSort, | ||
92 | setDefaultPagination, | ||
93 | asyncMiddleware(getUserVideos) | ||
94 | ) | ||
95 | |||
96 | usersRouter.get('/me/videos/:videoId/rating', | ||
97 | authenticate, | ||
98 | asyncMiddleware(usersVideoRatingValidator), | ||
99 | asyncMiddleware(getUserVideoRating) | ||
100 | ) | ||
101 | |||
102 | usersRouter.get('/', | ||
103 | authenticate, | ||
104 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
105 | paginationValidator, | ||
106 | usersSortValidator, | ||
107 | setDefaultSort, | ||
108 | setDefaultPagination, | ||
109 | asyncMiddleware(listUsers) | ||
110 | ) | ||
111 | |||
112 | usersRouter.post('/:id/block', | ||
113 | authenticate, | ||
114 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
115 | asyncMiddleware(usersBlockingValidator), | ||
116 | asyncMiddleware(blockUser) | ||
117 | ) | ||
118 | usersRouter.post('/:id/unblock', | ||
119 | authenticate, | ||
120 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
121 | asyncMiddleware(usersBlockingValidator), | ||
122 | asyncMiddleware(unblockUser) | ||
123 | ) | ||
124 | |||
125 | usersRouter.get('/:id', | ||
126 | authenticate, | ||
127 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
128 | asyncMiddleware(usersGetValidator), | ||
129 | getUser | ||
130 | ) | ||
131 | |||
132 | usersRouter.post('/', | ||
133 | authenticate, | ||
134 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
135 | asyncMiddleware(usersAddValidator), | ||
136 | asyncRetryTransactionMiddleware(createUser) | ||
137 | ) | ||
138 | |||
139 | usersRouter.post('/register', | ||
140 | asyncMiddleware(ensureUserRegistrationAllowed), | ||
141 | ensureUserRegistrationAllowedForIP, | ||
142 | asyncMiddleware(usersRegisterValidator), | ||
143 | asyncRetryTransactionMiddleware(registerUser) | ||
144 | ) | ||
145 | |||
146 | usersRouter.put('/me', | ||
147 | authenticate, | ||
148 | usersUpdateMeValidator, | ||
149 | asyncMiddleware(updateMe) | ||
150 | ) | ||
151 | |||
152 | usersRouter.post('/me/avatar/pick', | ||
153 | authenticate, | ||
154 | reqAvatarFile, | ||
155 | updateAvatarValidator, | ||
156 | asyncMiddleware(updateMyAvatar) | ||
157 | ) | ||
158 | |||
159 | usersRouter.put('/:id', | ||
160 | authenticate, | ||
161 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
162 | asyncMiddleware(usersUpdateValidator), | ||
163 | asyncMiddleware(updateUser) | ||
164 | ) | ||
165 | |||
166 | usersRouter.delete('/:id', | ||
167 | authenticate, | ||
168 | ensureUserHasRight(UserRight.MANAGE_USERS), | ||
169 | asyncMiddleware(usersRemoveValidator), | ||
170 | asyncMiddleware(removeUser) | ||
171 | ) | ||
172 | |||
173 | usersRouter.post('/ask-reset-password', | ||
174 | asyncMiddleware(usersAskResetPasswordValidator), | ||
175 | asyncMiddleware(askResetUserPassword) | ||
176 | ) | ||
177 | |||
178 | usersRouter.post('/:id/reset-password', | ||
179 | asyncMiddleware(usersResetPasswordValidator), | ||
180 | asyncMiddleware(resetUserPassword) | ||
181 | ) | ||
182 | |||
183 | usersRouter.post('/token', | ||
184 | loginRateLimiter, | ||
185 | token, | ||
186 | success | ||
187 | ) | ||
188 | // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route | ||
189 | |||
190 | // --------------------------------------------------------------------------- | ||
191 | |||
192 | export { | ||
193 | usersRouter | ||
194 | } | ||
195 | |||
196 | // --------------------------------------------------------------------------- | ||
197 | |||
198 | async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
199 | const user = res.locals.oauth.token.User as UserModel | ||
200 | const resultList = await VideoModel.listUserVideosForApi( | ||
201 | user.Account.id, | ||
202 | req.query.start as number, | ||
203 | req.query.count as number, | ||
204 | req.query.sort as VideoSortField | ||
205 | ) | ||
206 | |||
207 | const additionalAttributes = { | ||
208 | waitTranscoding: true, | ||
209 | state: true, | ||
210 | scheduledUpdate: true, | ||
211 | blacklistInfo: true | ||
212 | } | ||
213 | return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes })) | ||
214 | } | ||
215 | |||
216 | async function getUserVideoImports (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
217 | const user = res.locals.oauth.token.User as UserModel | ||
218 | const resultList = await VideoImportModel.listUserVideoImportsForApi( | ||
219 | user.id, | ||
220 | req.query.start as number, | ||
221 | req.query.count as number, | ||
222 | req.query.sort | ||
223 | ) | ||
224 | |||
225 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
226 | } | ||
227 | |||
228 | async function createUser (req: express.Request, res: express.Response) { | ||
229 | const body: UserCreate = req.body | ||
230 | const userToCreate = new UserModel({ | ||
231 | username: body.username, | ||
232 | password: body.password, | ||
233 | email: body.email, | ||
234 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | ||
235 | autoPlayVideo: true, | ||
236 | role: body.role, | ||
237 | videoQuota: body.videoQuota | ||
238 | }) | ||
239 | |||
240 | const { user, account } = await createUserAccountAndChannel(userToCreate) | ||
241 | |||
242 | auditLogger.create(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON())) | ||
243 | logger.info('User %s with its channel and account created.', body.username) | ||
244 | |||
245 | return res.json({ | ||
246 | user: { | ||
247 | id: user.id, | ||
248 | account: { | ||
249 | id: account.id, | ||
250 | uuid: account.Actor.uuid | ||
251 | } | ||
252 | } | ||
253 | }).end() | ||
254 | } | ||
255 | |||
256 | async function registerUser (req: express.Request, res: express.Response) { | ||
257 | const body: UserCreate = req.body | ||
258 | |||
259 | const userToCreate = new UserModel({ | ||
260 | username: body.username, | ||
261 | password: body.password, | ||
262 | email: body.email, | ||
263 | nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | ||
264 | autoPlayVideo: true, | ||
265 | role: UserRole.USER, | ||
266 | videoQuota: CONFIG.USER.VIDEO_QUOTA | ||
267 | }) | ||
268 | |||
269 | const { user } = await createUserAccountAndChannel(userToCreate) | ||
270 | |||
271 | auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON())) | ||
272 | logger.info('User %s with its channel and account registered.', body.username) | ||
273 | |||
274 | return res.type('json').status(204).end() | ||
275 | } | ||
276 | |||
277 | async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
278 | // We did not load channels in res.locals.user | ||
279 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) | ||
280 | |||
281 | return res.json(user.toFormattedJSON()) | ||
282 | } | ||
283 | |||
284 | async function getUserVideoQuotaUsed (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
285 | // We did not load channels in res.locals.user | ||
286 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) | ||
287 | const videoQuotaUsed = await UserModel.getOriginalVideoFileTotalFromUser(user) | ||
288 | |||
289 | const data: UserVideoQuota = { | ||
290 | videoQuotaUsed | ||
291 | } | ||
292 | return res.json(data) | ||
293 | } | ||
294 | |||
295 | async function unblockUser (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
296 | const user: UserModel = res.locals.user | ||
297 | |||
298 | await changeUserBlock(res, user, false) | ||
299 | |||
300 | return res.status(204).end() | ||
301 | } | ||
302 | |||
303 | async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
304 | const user: UserModel = res.locals.user | ||
305 | const reason = req.body.reason | ||
306 | |||
307 | await changeUserBlock(res, user, true, reason) | ||
308 | |||
309 | return res.status(204).end() | ||
310 | } | ||
311 | |||
312 | function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
313 | return res.json((res.locals.user as UserModel).toFormattedJSON()) | ||
314 | } | ||
315 | |||
316 | async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
317 | const videoId = +req.params.videoId | ||
318 | const accountId = +res.locals.oauth.token.User.Account.id | ||
319 | |||
320 | const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null) | ||
321 | const rating = ratingObj ? ratingObj.type : 'none' | ||
322 | |||
323 | const json: FormattedUserVideoRate = { | ||
324 | videoId, | ||
325 | rating | ||
326 | } | ||
327 | res.json(json) | ||
328 | } | ||
329 | |||
330 | async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
331 | const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort) | ||
332 | |||
333 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
334 | } | ||
335 | |||
336 | async function deleteMe (req: express.Request, res: express.Response) { | ||
337 | const user: UserModel = res.locals.oauth.token.User | ||
338 | |||
339 | await user.destroy() | ||
340 | |||
341 | auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON())) | ||
342 | |||
343 | return res.sendStatus(204) | ||
344 | } | ||
345 | |||
346 | async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
347 | const user: UserModel = res.locals.user | ||
348 | |||
349 | await user.destroy() | ||
350 | |||
351 | auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON())) | ||
352 | |||
353 | return res.sendStatus(204) | ||
354 | } | ||
355 | |||
356 | async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
357 | const body: UserUpdateMe = req.body | ||
358 | |||
359 | const user: UserModel = res.locals.oauth.token.user | ||
360 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | ||
361 | |||
362 | if (body.password !== undefined) user.password = body.password | ||
363 | if (body.email !== undefined) user.email = body.email | ||
364 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy | ||
365 | if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo | ||
366 | |||
367 | await sequelizeTypescript.transaction(async t => { | ||
368 | await user.save({ transaction: t }) | ||
369 | |||
370 | if (body.displayName !== undefined) user.Account.name = body.displayName | ||
371 | if (body.description !== undefined) user.Account.description = body.description | ||
372 | await user.Account.save({ transaction: t }) | ||
373 | |||
374 | await sendUpdateActor(user.Account, t) | ||
375 | |||
376 | auditLogger.update( | ||
377 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | ||
378 | new UserAuditView(user.toFormattedJSON()), | ||
379 | oldUserAuditView | ||
380 | ) | ||
381 | }) | ||
382 | |||
383 | return res.sendStatus(204) | ||
384 | } | ||
385 | |||
386 | async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
387 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | ||
388 | const user: UserModel = res.locals.oauth.token.user | ||
389 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | ||
390 | const account = user.Account | ||
391 | |||
392 | const avatar = await updateActorAvatarFile(avatarPhysicalFile, account.Actor, account) | ||
393 | |||
394 | auditLogger.update( | ||
395 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | ||
396 | new UserAuditView(user.toFormattedJSON()), | ||
397 | oldUserAuditView | ||
398 | ) | ||
399 | |||
400 | return res | ||
401 | .json({ | ||
402 | avatar: avatar.toFormattedJSON() | ||
403 | }) | ||
404 | .end() | ||
405 | } | ||
406 | |||
407 | async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
408 | const body: UserUpdate = req.body | ||
409 | const userToUpdate = res.locals.user as UserModel | ||
410 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) | ||
411 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role | ||
412 | |||
413 | if (body.email !== undefined) userToUpdate.email = body.email | ||
414 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota | ||
415 | if (body.role !== undefined) userToUpdate.role = body.role | ||
416 | |||
417 | const user = await userToUpdate.save() | ||
418 | |||
419 | // Destroy user token to refresh rights | ||
420 | if (roleChanged) { | ||
421 | await OAuthTokenModel.deleteUserToken(userToUpdate.id) | ||
422 | } | ||
423 | |||
424 | auditLogger.update( | ||
425 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | ||
426 | new UserAuditView(user.toFormattedJSON()), | ||
427 | oldUserAuditView | ||
428 | ) | ||
429 | |||
430 | // Don't need to send this update to followers, these attributes are not propagated | ||
431 | |||
432 | return res.sendStatus(204) | ||
433 | } | ||
434 | |||
435 | async function askResetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
436 | const user = res.locals.user as UserModel | ||
437 | |||
438 | const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) | ||
439 | const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString | ||
440 | await Emailer.Instance.addForgetPasswordEmailJob(user.email, url) | ||
441 | |||
442 | return res.status(204).end() | ||
443 | } | ||
444 | |||
445 | async function resetUserPassword (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
446 | const user = res.locals.user as UserModel | ||
447 | user.password = req.body.password | ||
448 | |||
449 | await user.save() | ||
450 | |||
451 | return res.status(204).end() | ||
452 | } | ||
453 | |||
454 | function success (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
455 | res.end() | ||
456 | } | ||
457 | |||
458 | async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) { | ||
459 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | ||
460 | |||
461 | user.blocked = block | ||
462 | user.blockedReason = reason || null | ||
463 | |||
464 | await sequelizeTypescript.transaction(async t => { | ||
465 | await OAuthTokenModel.deleteUserToken(user.id, t) | ||
466 | |||
467 | await user.save({ transaction: t }) | ||
468 | }) | ||
469 | |||
470 | await Emailer.Instance.addUserBlockJob(user, block, reason) | ||
471 | |||
472 | auditLogger.update( | ||
473 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | ||
474 | new UserAuditView(user.toFormattedJSON()), | ||
475 | oldUserAuditView | ||
476 | ) | ||
477 | } | ||