]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/middlewares/validators/videos.ts
Handle express-validator error on the client side and fix #96 (#98)
[github/Chocobozzz/PeerTube.git] / server / middlewares / validators / videos.ts
1 import { body, param, query } from 'express-validator/check'
2 import * as express from 'express'
3
4 import { database as db } from '../../initializers/database'
5 import { checkErrors } from './utils'
6 import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
7 import {
8 logger,
9 isVideoDurationValid,
10 isVideoFile,
11 isVideoNameValid,
12 isVideoCategoryValid,
13 isVideoLicenceValid,
14 isVideoDescriptionValid,
15 isVideoLanguageValid,
16 isVideoTagsValid,
17 isVideoNSFWValid,
18 isVideoIdOrUUIDValid,
19 isVideoAbuseReasonValid,
20 isVideoRatingTypeValid,
21 getDurationFromVideoFile,
22 checkVideoExists
23 } from '../../helpers'
24
25 const videosAddValidator = [
26 body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
27 'This file is not supported. Are you sure it is of the following type : '
28 + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME
29 ),
30 body('name').custom(isVideoNameValid).withMessage('Should have a valid name'),
31 body('category').custom(isVideoCategoryValid).withMessage('Should have a valid category'),
32 body('licence').custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
33 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
34 body('nsfw').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
35 body('description').custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
36 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
37
38 (req: express.Request, res: express.Response, next: express.NextFunction) => {
39 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
40
41 checkErrors(req, res, () => {
42 const videoFile: Express.Multer.File = req.files['videofile'][0]
43 const user = res.locals.oauth.token.User
44
45 user.isAbleToUploadVideo(videoFile)
46 .then(isAble => {
47 if (isAble === false) {
48 res.status(403)
49 .json({ error: 'The user video quota is exceeded with this video.' })
50 .end()
51
52 return undefined
53 }
54
55 return getDurationFromVideoFile(videoFile.path)
56 .catch(err => {
57 logger.error('Invalid input file in videosAddValidator.', err)
58 res.status(400)
59 .json({ error: 'Invalid input file.' })
60 .end()
61
62 return undefined
63 })
64 })
65 .then(duration => {
66 // Previous test failed, abort
67 if (duration === undefined) return
68
69 if (!isVideoDurationValid('' + duration)) {
70 return res.status(400)
71 .json({
72 error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).'
73 })
74 .end()
75 }
76
77 videoFile['duration'] = duration
78 next()
79 })
80 .catch(err => {
81 logger.error('Error in video add validator', err)
82 res.sendStatus(500)
83
84 return undefined
85 })
86 })
87 }
88 ]
89
90 const videosUpdateValidator = [
91 param('id').custom(isVideoIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
92 body('name').optional().custom(isVideoNameValid).withMessage('Should have a valid name'),
93 body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
94 body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
95 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
96 body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
97 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
98 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
99
100 (req: express.Request, res: express.Response, next: express.NextFunction) => {
101 logger.debug('Checking videosUpdate parameters', { parameters: req.body })
102
103 checkErrors(req, res, () => {
104 checkVideoExists(req.params.id, res, () => {
105 // We need to make additional checks
106 if (res.locals.video.isOwned() === false) {
107 return res.status(403)
108 .json({ error: 'Cannot update video of another pod' })
109 .end()
110 }
111
112 if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
113 return res.status(403)
114 .json({ error: 'Cannot update video of another user' })
115 .end()
116 }
117
118 next()
119 })
120 })
121 }
122 ]
123
124 const videosGetValidator = [
125 param('id').custom(isVideoIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
126
127 (req: express.Request, res: express.Response, next: express.NextFunction) => {
128 logger.debug('Checking videosGet parameters', { parameters: req.params })
129
130 checkErrors(req, res, () => {
131 checkVideoExists(req.params.id, res, next)
132 })
133 }
134 ]
135
136 const videosRemoveValidator = [
137 param('id').custom(isVideoIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
138
139 (req: express.Request, res: express.Response, next: express.NextFunction) => {
140 logger.debug('Checking videosRemove parameters', { parameters: req.params })
141
142 checkErrors(req, res, () => {
143 checkVideoExists(req.params.id, res, () => {
144 // Check if the user who did the request is able to delete the video
145 checkUserCanDeleteVideo(res.locals.oauth.token.User.id, res, () => {
146 next()
147 })
148 })
149 })
150 }
151 ]
152
153 const videosSearchValidator = [
154 param('value').not().isEmpty().withMessage('Should have a valid search'),
155 query('field').optional().isIn(SEARCHABLE_COLUMNS.VIDEOS).withMessage('Should have correct searchable column'),
156
157 (req: express.Request, res: express.Response, next: express.NextFunction) => {
158 logger.debug('Checking videosSearch parameters', { parameters: req.params })
159
160 checkErrors(req, res, next)
161 }
162 ]
163
164 const videoAbuseReportValidator = [
165 param('id').custom(isVideoIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
166 body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'),
167
168 (req: express.Request, res: express.Response, next: express.NextFunction) => {
169 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
170
171 checkErrors(req, res, () => {
172 checkVideoExists(req.params.id, res, next)
173 })
174 }
175 ]
176
177 const videoRateValidator = [
178 param('id').custom(isVideoIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
179 body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
180
181 (req: express.Request, res: express.Response, next: express.NextFunction) => {
182 logger.debug('Checking videoRate parameters', { parameters: req.body })
183
184 checkErrors(req, res, () => {
185 checkVideoExists(req.params.id, res, next)
186 })
187 }
188 ]
189
190 // ---------------------------------------------------------------------------
191
192 export {
193 videosAddValidator,
194 videosUpdateValidator,
195 videosGetValidator,
196 videosRemoveValidator,
197 videosSearchValidator,
198
199 videoAbuseReportValidator,
200
201 videoRateValidator
202 }
203
204 // ---------------------------------------------------------------------------
205
206 function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) {
207 // Retrieve the user who did the request
208 db.User.loadById(userId)
209 .then(user => {
210 if (res.locals.video.isOwned() === false) {
211 return res.status(403)
212 .json({ error: 'Cannot remove video of another pod, blacklist it' })
213 .end()
214 }
215
216 // Check if the user can delete the video
217 // The user can delete it if s/he is an admin
218 // Or if s/he is the video's author
219 if (user.isAdmin() === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
220 return res.status(403)
221 .json({ error: 'Cannot remove video of another user' })
222 .end()
223 }
224
225 // If we reach this comment, we can delete the video
226 callback()
227 })
228 .catch(err => {
229 logger.error('Error in video request validator.', err)
230 return res.sendStatus(500)
231 })
232 }