]>
Commit | Line | Data |
---|---|---|
d03cd8bb C |
1 | import * as express from 'express' |
2 | import 'multer' | |
3 | import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' | |
4 | import { getFormattedObjects } from '../../../helpers/utils' | |
5 | import { CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../../initializers' | |
6 | import { sendUpdateActor } from '../../../lib/activitypub/send' | |
7 | import { | |
8 | asyncMiddleware, | |
9 | authenticate, | |
06a05d5f | 10 | commonVideosFiltersValidator, |
d03cd8bb C |
11 | paginationValidator, |
12 | setDefaultPagination, | |
13 | setDefaultSort, | |
06a05d5f C |
14 | userSubscriptionAddValidator, |
15 | userSubscriptionRemoveValidator, | |
d03cd8bb C |
16 | usersUpdateMeValidator, |
17 | usersVideoRatingValidator | |
18 | } from '../../../middlewares' | |
06a05d5f C |
19 | import { |
20 | deleteMeValidator, | |
21 | userSubscriptionsSortValidator, | |
22 | videoImportsSortValidator, | |
23 | videosSortValidator | |
24 | } from '../../../middlewares/validators' | |
d03cd8bb C |
25 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
26 | import { UserModel } from '../../../models/account/user' | |
27 | import { VideoModel } from '../../../models/video/video' | |
28 | import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type' | |
06a05d5f | 29 | import { buildNSFWFilter, createReqFiles } from '../../../helpers/express-utils' |
d03cd8bb C |
30 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' |
31 | import { updateAvatarValidator } from '../../../middlewares/validators/avatar' | |
32 | import { updateActorAvatarFile } from '../../../lib/avatar' | |
33 | import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger' | |
34 | import { VideoImportModel } from '../../../models/video/video-import' | |
06a05d5f C |
35 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' |
36 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | |
37 | import { JobQueue } from '../../../lib/job-queue' | |
38 | import { logger } from '../../../helpers/logger' | |
d03cd8bb C |
39 | |
40 | const auditLogger = auditLoggerFactory('users-me') | |
41 | ||
42 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) | |
43 | ||
44 | const meRouter = express.Router() | |
45 | ||
46 | meRouter.get('/me', | |
47 | authenticate, | |
48 | asyncMiddleware(getUserInformation) | |
49 | ) | |
50 | meRouter.delete('/me', | |
51 | authenticate, | |
52 | asyncMiddleware(deleteMeValidator), | |
53 | asyncMiddleware(deleteMe) | |
54 | ) | |
55 | ||
56 | meRouter.get('/me/video-quota-used', | |
57 | authenticate, | |
58 | asyncMiddleware(getUserVideoQuotaUsed) | |
59 | ) | |
60 | ||
61 | meRouter.get('/me/videos/imports', | |
62 | authenticate, | |
63 | paginationValidator, | |
64 | videoImportsSortValidator, | |
65 | setDefaultSort, | |
66 | setDefaultPagination, | |
67 | asyncMiddleware(getUserVideoImports) | |
68 | ) | |
69 | ||
70 | meRouter.get('/me/videos', | |
71 | authenticate, | |
72 | paginationValidator, | |
73 | videosSortValidator, | |
74 | setDefaultSort, | |
75 | setDefaultPagination, | |
76 | asyncMiddleware(getUserVideos) | |
77 | ) | |
78 | ||
79 | meRouter.get('/me/videos/:videoId/rating', | |
80 | authenticate, | |
81 | asyncMiddleware(usersVideoRatingValidator), | |
82 | asyncMiddleware(getUserVideoRating) | |
83 | ) | |
84 | ||
85 | meRouter.put('/me', | |
86 | authenticate, | |
87 | usersUpdateMeValidator, | |
88 | asyncMiddleware(updateMe) | |
89 | ) | |
90 | ||
91 | meRouter.post('/me/avatar/pick', | |
92 | authenticate, | |
93 | reqAvatarFile, | |
94 | updateAvatarValidator, | |
95 | asyncMiddleware(updateMyAvatar) | |
96 | ) | |
97 | ||
06a05d5f C |
98 | // ##### Subscriptions part ##### |
99 | ||
100 | meRouter.get('/me/subscriptions', | |
101 | authenticate, | |
102 | paginationValidator, | |
103 | userSubscriptionsSortValidator, | |
104 | setDefaultSort, | |
105 | setDefaultPagination, | |
106 | asyncMiddleware(getUserSubscriptions) | |
107 | ) | |
108 | ||
109 | meRouter.post('/me/subscriptions', | |
110 | authenticate, | |
111 | userSubscriptionAddValidator, | |
112 | asyncMiddleware(addUserSubscription) | |
113 | ) | |
114 | ||
115 | meRouter.delete('/me/subscriptions/:uri', | |
116 | authenticate, | |
117 | userSubscriptionRemoveValidator, | |
118 | asyncMiddleware(deleteUserSubscription) | |
119 | ) | |
120 | ||
121 | meRouter.get('/me/subscriptions/videos', | |
122 | authenticate, | |
123 | authenticate, | |
124 | paginationValidator, | |
125 | videosSortValidator, | |
126 | setDefaultSort, | |
127 | setDefaultPagination, | |
128 | commonVideosFiltersValidator, | |
129 | asyncMiddleware(getUserSubscriptionVideos) | |
130 | ) | |
131 | ||
d03cd8bb C |
132 | // --------------------------------------------------------------------------- |
133 | ||
134 | export { | |
135 | meRouter | |
136 | } | |
137 | ||
138 | // --------------------------------------------------------------------------- | |
139 | ||
06a05d5f C |
140 | async function addUserSubscription (req: express.Request, res: express.Response) { |
141 | const user = res.locals.oauth.token.User as UserModel | |
142 | const [ name, host ] = req.body.uri.split('@') | |
143 | ||
144 | const payload = { | |
145 | name, | |
146 | host, | |
147 | followerActorId: user.Account.Actor.id | |
148 | } | |
149 | ||
150 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | |
151 | .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err)) | |
152 | ||
153 | return res.status(204).end() | |
154 | } | |
155 | ||
156 | async function deleteUserSubscription (req: express.Request, res: express.Response) { | |
157 | const subscription: ActorFollowModel = res.locals.subscription | |
158 | ||
159 | await sequelizeTypescript.transaction(async t => { | |
160 | return subscription.destroy({ transaction: t }) | |
161 | }) | |
162 | ||
163 | return res.type('json').status(204).end() | |
164 | } | |
165 | ||
166 | async function getUserSubscriptions (req: express.Request, res: express.Response) { | |
167 | const user = res.locals.oauth.token.User as UserModel | |
168 | const actorId = user.Account.Actor.id | |
169 | ||
170 | const resultList = await ActorFollowModel.listSubscriptionsForApi(actorId, req.query.start, req.query.count, req.query.sort) | |
171 | ||
172 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | |
173 | } | |
174 | ||
175 | async function getUserSubscriptionVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | |
176 | const user = res.locals.oauth.token.User as UserModel | |
177 | const resultList = await VideoModel.listForApi({ | |
178 | start: req.query.start, | |
179 | count: req.query.count, | |
180 | sort: req.query.sort, | |
181 | includeLocalVideos: false, | |
182 | categoryOneOf: req.query.categoryOneOf, | |
183 | licenceOneOf: req.query.licenceOneOf, | |
184 | languageOneOf: req.query.languageOneOf, | |
185 | tagsOneOf: req.query.tagsOneOf, | |
186 | tagsAllOf: req.query.tagsAllOf, | |
187 | nsfw: buildNSFWFilter(res, req.query.nsfw), | |
188 | filter: req.query.filter as VideoFilter, | |
189 | withFiles: false, | |
190 | actorId: user.Account.Actor.id | |
191 | }) | |
192 | ||
193 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | |
194 | } | |
195 | ||
d03cd8bb C |
196 | async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) { |
197 | const user = res.locals.oauth.token.User as UserModel | |
198 | const resultList = await VideoModel.listUserVideosForApi( | |
199 | user.Account.id, | |
200 | req.query.start as number, | |
201 | req.query.count as number, | |
202 | req.query.sort as VideoSortField | |
203 | ) | |
204 | ||
205 | const additionalAttributes = { | |
206 | waitTranscoding: true, | |
207 | state: true, | |
208 | scheduledUpdate: true, | |
209 | blacklistInfo: true | |
210 | } | |
211 | return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes })) | |
212 | } | |
213 | ||
214 | async function getUserVideoImports (req: express.Request, res: express.Response, next: express.NextFunction) { | |
215 | const user = res.locals.oauth.token.User as UserModel | |
216 | const resultList = await VideoImportModel.listUserVideoImportsForApi( | |
217 | user.id, | |
218 | req.query.start as number, | |
219 | req.query.count as number, | |
220 | req.query.sort | |
221 | ) | |
222 | ||
223 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | |
224 | } | |
225 | ||
226 | async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) { | |
227 | // We did not load channels in res.locals.user | |
228 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) | |
229 | ||
230 | return res.json(user.toFormattedJSON()) | |
231 | } | |
232 | ||
233 | async function getUserVideoQuotaUsed (req: express.Request, res: express.Response, next: express.NextFunction) { | |
234 | // We did not load channels in res.locals.user | |
235 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) | |
236 | const videoQuotaUsed = await UserModel.getOriginalVideoFileTotalFromUser(user) | |
237 | ||
238 | const data: UserVideoQuota = { | |
239 | videoQuotaUsed | |
240 | } | |
241 | return res.json(data) | |
242 | } | |
243 | ||
244 | async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) { | |
245 | const videoId = +req.params.videoId | |
246 | const accountId = +res.locals.oauth.token.User.Account.id | |
247 | ||
248 | const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null) | |
249 | const rating = ratingObj ? ratingObj.type : 'none' | |
250 | ||
251 | const json: FormattedUserVideoRate = { | |
252 | videoId, | |
253 | rating | |
254 | } | |
06a05d5f | 255 | return res.json(json) |
d03cd8bb C |
256 | } |
257 | ||
258 | async function deleteMe (req: express.Request, res: express.Response) { | |
259 | const user: UserModel = res.locals.oauth.token.User | |
260 | ||
261 | await user.destroy() | |
262 | ||
263 | auditLogger.delete(res.locals.oauth.token.User.Account.Actor.getIdentifier(), new UserAuditView(user.toFormattedJSON())) | |
264 | ||
265 | return res.sendStatus(204) | |
266 | } | |
267 | ||
268 | async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { | |
269 | const body: UserUpdateMe = req.body | |
270 | ||
271 | const user: UserModel = res.locals.oauth.token.user | |
272 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | |
273 | ||
274 | if (body.password !== undefined) user.password = body.password | |
275 | if (body.email !== undefined) user.email = body.email | |
276 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy | |
277 | if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo | |
278 | ||
279 | await sequelizeTypescript.transaction(async t => { | |
280 | await user.save({ transaction: t }) | |
281 | ||
282 | if (body.displayName !== undefined) user.Account.name = body.displayName | |
283 | if (body.description !== undefined) user.Account.description = body.description | |
284 | await user.Account.save({ transaction: t }) | |
285 | ||
286 | await sendUpdateActor(user.Account, t) | |
287 | ||
288 | auditLogger.update( | |
289 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | |
290 | new UserAuditView(user.toFormattedJSON()), | |
291 | oldUserAuditView | |
292 | ) | |
293 | }) | |
294 | ||
295 | return res.sendStatus(204) | |
296 | } | |
297 | ||
298 | async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) { | |
299 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | |
300 | const user: UserModel = res.locals.oauth.token.user | |
301 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | |
302 | const account = user.Account | |
303 | ||
304 | const avatar = await updateActorAvatarFile(avatarPhysicalFile, account.Actor, account) | |
305 | ||
306 | auditLogger.update( | |
307 | res.locals.oauth.token.User.Account.Actor.getIdentifier(), | |
308 | new UserAuditView(user.toFormattedJSON()), | |
309 | oldUserAuditView | |
310 | ) | |
311 | ||
06a05d5f | 312 | return res.json({ avatar: avatar.toFormattedJSON() }) |
d03cd8bb | 313 | } |