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