diff options
author | Chocobozzz <me@florianbigard.com> | 2018-10-05 11:15:06 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-10-05 11:22:38 +0200 |
commit | 6e46de095d7169355dd83030f6ce4a582304153a (patch) | |
tree | dfa78e2008d3d135a00b798b05350b4975145acc /server/middlewares/validators/videos | |
parent | a585824160d016db7c9bff0e1cb1ffa3aaf73d74 (diff) | |
download | PeerTube-6e46de095d7169355dd83030f6ce4a582304153a.tar.gz PeerTube-6e46de095d7169355dd83030f6ce4a582304153a.tar.zst PeerTube-6e46de095d7169355dd83030f6ce4a582304153a.zip |
Add user history and resume videos
Diffstat (limited to 'server/middlewares/validators/videos')
-rw-r--r-- | server/middlewares/validators/videos/index.ts | 8 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-abuses.ts | 71 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-blacklist.ts | 62 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-captions.ts | 71 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-channels.ts | 175 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-comments.ts | 195 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-imports.ts | 75 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-watch.ts | 28 | ||||
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 399 |
9 files changed, 1084 insertions, 0 deletions
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts new file mode 100644 index 000000000..294783d85 --- /dev/null +++ b/server/middlewares/validators/videos/index.ts | |||
@@ -0,0 +1,8 @@ | |||
1 | export * from './video-abuses' | ||
2 | export * from './video-blacklist' | ||
3 | export * from './video-captions' | ||
4 | export * from './video-channels' | ||
5 | export * from './video-comments' | ||
6 | export * from './video-imports' | ||
7 | export * from './video-watch' | ||
8 | export * from './videos' | ||
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts new file mode 100644 index 000000000..be26ca16a --- /dev/null +++ b/server/middlewares/validators/videos/video-abuses.ts | |||
@@ -0,0 +1,71 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'express-validator' | ||
3 | import { body, param } from 'express-validator/check' | ||
4 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' | ||
5 | import { isVideoExist } from '../../../helpers/custom-validators/videos' | ||
6 | import { logger } from '../../../helpers/logger' | ||
7 | import { areValidationErrors } from '../utils' | ||
8 | import { | ||
9 | isVideoAbuseExist, | ||
10 | isVideoAbuseModerationCommentValid, | ||
11 | isVideoAbuseReasonValid, | ||
12 | isVideoAbuseStateValid | ||
13 | } from '../../../helpers/custom-validators/video-abuses' | ||
14 | |||
15 | const videoAbuseReportValidator = [ | ||
16 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
17 | body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'), | ||
18 | |||
19 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
20 | logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) | ||
21 | |||
22 | if (areValidationErrors(req, res)) return | ||
23 | if (!await isVideoExist(req.params.videoId, res)) return | ||
24 | |||
25 | return next() | ||
26 | } | ||
27 | ] | ||
28 | |||
29 | const videoAbuseGetValidator = [ | ||
30 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
31 | param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'), | ||
32 | |||
33 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
34 | logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body }) | ||
35 | |||
36 | if (areValidationErrors(req, res)) return | ||
37 | if (!await isVideoExist(req.params.videoId, res)) return | ||
38 | if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return | ||
39 | |||
40 | return next() | ||
41 | } | ||
42 | ] | ||
43 | |||
44 | const videoAbuseUpdateValidator = [ | ||
45 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
46 | param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'), | ||
47 | body('state') | ||
48 | .optional() | ||
49 | .custom(isVideoAbuseStateValid).withMessage('Should have a valid video abuse state'), | ||
50 | body('moderationComment') | ||
51 | .optional() | ||
52 | .custom(isVideoAbuseModerationCommentValid).withMessage('Should have a valid video moderation comment'), | ||
53 | |||
54 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
55 | logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body }) | ||
56 | |||
57 | if (areValidationErrors(req, res)) return | ||
58 | if (!await isVideoExist(req.params.videoId, res)) return | ||
59 | if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return | ||
60 | |||
61 | return next() | ||
62 | } | ||
63 | ] | ||
64 | |||
65 | // --------------------------------------------------------------------------- | ||
66 | |||
67 | export { | ||
68 | videoAbuseReportValidator, | ||
69 | videoAbuseGetValidator, | ||
70 | videoAbuseUpdateValidator | ||
71 | } | ||
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts new file mode 100644 index 000000000..13da7acff --- /dev/null +++ b/server/middlewares/validators/videos/video-blacklist.ts | |||
@@ -0,0 +1,62 @@ | |||
1 | import * as express from 'express' | ||
2 | import { body, param } from 'express-validator/check' | ||
3 | import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { isVideoExist } from '../../../helpers/custom-validators/videos' | ||
5 | import { logger } from '../../../helpers/logger' | ||
6 | import { areValidationErrors } from '../utils' | ||
7 | import { isVideoBlacklistExist, isVideoBlacklistReasonValid } from '../../../helpers/custom-validators/video-blacklist' | ||
8 | |||
9 | const videosBlacklistRemoveValidator = [ | ||
10 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
11 | |||
12 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
13 | logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) | ||
14 | |||
15 | if (areValidationErrors(req, res)) return | ||
16 | if (!await isVideoExist(req.params.videoId, res)) return | ||
17 | if (!await isVideoBlacklistExist(res.locals.video.id, res)) return | ||
18 | |||
19 | return next() | ||
20 | } | ||
21 | ] | ||
22 | |||
23 | const videosBlacklistAddValidator = [ | ||
24 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
25 | body('reason') | ||
26 | .optional() | ||
27 | .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), | ||
28 | |||
29 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
30 | logger.debug('Checking videosBlacklistAdd parameters', { parameters: req.params }) | ||
31 | |||
32 | if (areValidationErrors(req, res)) return | ||
33 | if (!await isVideoExist(req.params.videoId, res)) return | ||
34 | |||
35 | return next() | ||
36 | } | ||
37 | ] | ||
38 | |||
39 | const videosBlacklistUpdateValidator = [ | ||
40 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
41 | body('reason') | ||
42 | .optional() | ||
43 | .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), | ||
44 | |||
45 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
46 | logger.debug('Checking videosBlacklistUpdate parameters', { parameters: req.params }) | ||
47 | |||
48 | if (areValidationErrors(req, res)) return | ||
49 | if (!await isVideoExist(req.params.videoId, res)) return | ||
50 | if (!await isVideoBlacklistExist(res.locals.video.id, res)) return | ||
51 | |||
52 | return next() | ||
53 | } | ||
54 | ] | ||
55 | |||
56 | // --------------------------------------------------------------------------- | ||
57 | |||
58 | export { | ||
59 | videosBlacklistAddValidator, | ||
60 | videosBlacklistRemoveValidator, | ||
61 | videosBlacklistUpdateValidator | ||
62 | } | ||
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts new file mode 100644 index 000000000..63d84fbec --- /dev/null +++ b/server/middlewares/validators/videos/video-captions.ts | |||
@@ -0,0 +1,71 @@ | |||
1 | import * as express from 'express' | ||
2 | import { areValidationErrors } from '../utils' | ||
3 | import { checkUserCanManageVideo, isVideoExist } from '../../../helpers/custom-validators/videos' | ||
4 | import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' | ||
5 | import { body, param } from 'express-validator/check' | ||
6 | import { CONSTRAINTS_FIELDS } from '../../../initializers' | ||
7 | import { UserRight } from '../../../../shared' | ||
8 | import { logger } from '../../../helpers/logger' | ||
9 | import { isVideoCaptionExist, isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' | ||
10 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | ||
11 | |||
12 | const addVideoCaptionValidator = [ | ||
13 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | ||
14 | param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | ||
15 | body('captionfile') | ||
16 | .custom((value, { req }) => isVideoCaptionFile(req.files, 'captionfile')).withMessage( | ||
17 | 'This caption file is not supported or too large. Please, make sure it is of the following type : ' | ||
18 | + CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.EXTNAME.join(', ') | ||
19 | ), | ||
20 | |||
21 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
22 | logger.debug('Checking addVideoCaption parameters', { parameters: req.body }) | ||
23 | |||
24 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | ||
25 | if (!await isVideoExist(req.params.videoId, res)) return cleanUpReqFiles(req) | ||
26 | |||
27 | // Check if the user who did the request is able to update the video | ||
28 | const user = res.locals.oauth.token.User | ||
29 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) | ||
30 | |||
31 | return next() | ||
32 | } | ||
33 | ] | ||
34 | |||
35 | const deleteVideoCaptionValidator = [ | ||
36 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | ||
37 | param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | ||
38 | |||
39 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
40 | logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params }) | ||
41 | |||
42 | if (areValidationErrors(req, res)) return | ||
43 | if (!await isVideoExist(req.params.videoId, res)) return | ||
44 | if (!await isVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return | ||
45 | |||
46 | // Check if the user who did the request is able to update the video | ||
47 | const user = res.locals.oauth.token.User | ||
48 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return | ||
49 | |||
50 | return next() | ||
51 | } | ||
52 | ] | ||
53 | |||
54 | const listVideoCaptionsValidator = [ | ||
55 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | ||
56 | |||
57 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
58 | logger.debug('Checking listVideoCaptions parameters', { parameters: req.params }) | ||
59 | |||
60 | if (areValidationErrors(req, res)) return | ||
61 | if (!await isVideoExist(req.params.videoId, res, 'id')) return | ||
62 | |||
63 | return next() | ||
64 | } | ||
65 | ] | ||
66 | |||
67 | export { | ||
68 | addVideoCaptionValidator, | ||
69 | listVideoCaptionsValidator, | ||
70 | deleteVideoCaptionValidator | ||
71 | } | ||
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts new file mode 100644 index 000000000..f039794e0 --- /dev/null +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -0,0 +1,175 @@ | |||
1 | import * as express from 'express' | ||
2 | import { body, param } from 'express-validator/check' | ||
3 | import { UserRight } from '../../../../shared' | ||
4 | import { isAccountNameWithHostExist } from '../../../helpers/custom-validators/accounts' | ||
5 | import { | ||
6 | isLocalVideoChannelNameExist, | ||
7 | isVideoChannelDescriptionValid, | ||
8 | isVideoChannelNameValid, | ||
9 | isVideoChannelNameWithHostExist, | ||
10 | isVideoChannelSupportValid | ||
11 | } from '../../../helpers/custom-validators/video-channels' | ||
12 | import { logger } from '../../../helpers/logger' | ||
13 | import { UserModel } from '../../../models/account/user' | ||
14 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
15 | import { areValidationErrors } from '../utils' | ||
16 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | ||
17 | import { ActorModel } from '../../../models/activitypub/actor' | ||
18 | |||
19 | const listVideoAccountChannelsValidator = [ | ||
20 | param('accountName').exists().withMessage('Should have a valid account name'), | ||
21 | |||
22 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
23 | logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) | ||
24 | |||
25 | if (areValidationErrors(req, res)) return | ||
26 | if (!await isAccountNameWithHostExist(req.params.accountName, res)) return | ||
27 | |||
28 | return next() | ||
29 | } | ||
30 | ] | ||
31 | |||
32 | const videoChannelsAddValidator = [ | ||
33 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | ||
34 | body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), | ||
35 | body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), | ||
36 | body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), | ||
37 | |||
38 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
39 | logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body }) | ||
40 | |||
41 | if (areValidationErrors(req, res)) return | ||
42 | |||
43 | const actor = await ActorModel.loadLocalByName(req.body.name) | ||
44 | if (actor) { | ||
45 | res.status(409) | ||
46 | .send({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' }) | ||
47 | .end() | ||
48 | return false | ||
49 | } | ||
50 | |||
51 | return next() | ||
52 | } | ||
53 | ] | ||
54 | |||
55 | const videoChannelsUpdateValidator = [ | ||
56 | param('nameWithHost').exists().withMessage('Should have an video channel name with host'), | ||
57 | body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), | ||
58 | body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), | ||
59 | body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), | ||
60 | |||
61 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
62 | logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) | ||
63 | |||
64 | if (areValidationErrors(req, res)) return | ||
65 | if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return | ||
66 | |||
67 | // We need to make additional checks | ||
68 | if (res.locals.videoChannel.Actor.isOwned() === false) { | ||
69 | return res.status(403) | ||
70 | .json({ error: 'Cannot update video channel of another server' }) | ||
71 | .end() | ||
72 | } | ||
73 | |||
74 | if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { | ||
75 | return res.status(403) | ||
76 | .json({ error: 'Cannot update video channel of another user' }) | ||
77 | .end() | ||
78 | } | ||
79 | |||
80 | return next() | ||
81 | } | ||
82 | ] | ||
83 | |||
84 | const videoChannelsRemoveValidator = [ | ||
85 | param('nameWithHost').exists().withMessage('Should have an video channel name with host'), | ||
86 | |||
87 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
88 | logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) | ||
89 | |||
90 | if (areValidationErrors(req, res)) return | ||
91 | if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return | ||
92 | |||
93 | if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return | ||
94 | if (!await checkVideoChannelIsNotTheLastOne(res)) return | ||
95 | |||
96 | return next() | ||
97 | } | ||
98 | ] | ||
99 | |||
100 | const videoChannelsNameWithHostValidator = [ | ||
101 | param('nameWithHost').exists().withMessage('Should have an video channel name with host'), | ||
102 | |||
103 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
104 | logger.debug('Checking videoChannelsNameWithHostValidator parameters', { parameters: req.params }) | ||
105 | |||
106 | if (areValidationErrors(req, res)) return | ||
107 | |||
108 | if (!await isVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return | ||
109 | |||
110 | return next() | ||
111 | } | ||
112 | ] | ||
113 | |||
114 | const localVideoChannelValidator = [ | ||
115 | param('name').custom(isVideoChannelNameValid).withMessage('Should have a valid video channel name'), | ||
116 | |||
117 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
118 | logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params }) | ||
119 | |||
120 | if (areValidationErrors(req, res)) return | ||
121 | if (!await isLocalVideoChannelNameExist(req.params.name, res)) return | ||
122 | |||
123 | return next() | ||
124 | } | ||
125 | ] | ||
126 | |||
127 | // --------------------------------------------------------------------------- | ||
128 | |||
129 | export { | ||
130 | listVideoAccountChannelsValidator, | ||
131 | videoChannelsAddValidator, | ||
132 | videoChannelsUpdateValidator, | ||
133 | videoChannelsRemoveValidator, | ||
134 | videoChannelsNameWithHostValidator, | ||
135 | localVideoChannelValidator | ||
136 | } | ||
137 | |||
138 | // --------------------------------------------------------------------------- | ||
139 | |||
140 | function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) { | ||
141 | if (videoChannel.Actor.isOwned() === false) { | ||
142 | res.status(403) | ||
143 | .json({ error: 'Cannot remove video channel of another server.' }) | ||
144 | .end() | ||
145 | |||
146 | return false | ||
147 | } | ||
148 | |||
149 | // Check if the user can delete the video channel | ||
150 | // The user can delete it if s/he is an admin | ||
151 | // Or if s/he is the video channel's account | ||
152 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) { | ||
153 | res.status(403) | ||
154 | .json({ error: 'Cannot remove video channel of another user' }) | ||
155 | .end() | ||
156 | |||
157 | return false | ||
158 | } | ||
159 | |||
160 | return true | ||
161 | } | ||
162 | |||
163 | async function checkVideoChannelIsNotTheLastOne (res: express.Response) { | ||
164 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) | ||
165 | |||
166 | if (count <= 1) { | ||
167 | res.status(409) | ||
168 | .json({ error: 'Cannot remove the last channel of this user' }) | ||
169 | .end() | ||
170 | |||
171 | return false | ||
172 | } | ||
173 | |||
174 | return true | ||
175 | } | ||
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts new file mode 100644 index 000000000..348d33082 --- /dev/null +++ b/server/middlewares/validators/videos/video-comments.ts | |||
@@ -0,0 +1,195 @@ | |||
1 | import * as express from 'express' | ||
2 | import { body, param } from 'express-validator/check' | ||
3 | import { UserRight } from '../../../../shared' | ||
4 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' | ||
5 | import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' | ||
6 | import { isVideoExist } from '../../../helpers/custom-validators/videos' | ||
7 | import { logger } from '../../../helpers/logger' | ||
8 | import { UserModel } from '../../../models/account/user' | ||
9 | import { VideoModel } from '../../../models/video/video' | ||
10 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
11 | import { areValidationErrors } from '../utils' | ||
12 | |||
13 | const listVideoCommentThreadsValidator = [ | ||
14 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
15 | |||
16 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
17 | logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params }) | ||
18 | |||
19 | if (areValidationErrors(req, res)) return | ||
20 | if (!await isVideoExist(req.params.videoId, res, 'only-video')) return | ||
21 | |||
22 | return next() | ||
23 | } | ||
24 | ] | ||
25 | |||
26 | const listVideoThreadCommentsValidator = [ | ||
27 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
28 | param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'), | ||
29 | |||
30 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
31 | logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params }) | ||
32 | |||
33 | if (areValidationErrors(req, res)) return | ||
34 | if (!await isVideoExist(req.params.videoId, res, 'only-video')) return | ||
35 | if (!await isVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return | ||
36 | |||
37 | return next() | ||
38 | } | ||
39 | ] | ||
40 | |||
41 | const addVideoCommentThreadValidator = [ | ||
42 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
43 | body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), | ||
44 | |||
45 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
46 | logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body }) | ||
47 | |||
48 | if (areValidationErrors(req, res)) return | ||
49 | if (!await isVideoExist(req.params.videoId, res)) return | ||
50 | if (!isVideoCommentsEnabled(res.locals.video, res)) return | ||
51 | |||
52 | return next() | ||
53 | } | ||
54 | ] | ||
55 | |||
56 | const addVideoCommentReplyValidator = [ | ||
57 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
58 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | ||
59 | body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), | ||
60 | |||
61 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
62 | logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params, body: req.body }) | ||
63 | |||
64 | if (areValidationErrors(req, res)) return | ||
65 | if (!await isVideoExist(req.params.videoId, res)) return | ||
66 | if (!isVideoCommentsEnabled(res.locals.video, res)) return | ||
67 | if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return | ||
68 | |||
69 | return next() | ||
70 | } | ||
71 | ] | ||
72 | |||
73 | const videoCommentGetValidator = [ | ||
74 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
75 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | ||
76 | |||
77 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
78 | logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) | ||
79 | |||
80 | if (areValidationErrors(req, res)) return | ||
81 | if (!await isVideoExist(req.params.videoId, res, 'id')) return | ||
82 | if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return | ||
83 | |||
84 | return next() | ||
85 | } | ||
86 | ] | ||
87 | |||
88 | const removeVideoCommentValidator = [ | ||
89 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | ||
90 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | ||
91 | |||
92 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
93 | logger.debug('Checking removeVideoCommentValidator parameters.', { parameters: req.params }) | ||
94 | |||
95 | if (areValidationErrors(req, res)) return | ||
96 | if (!await isVideoExist(req.params.videoId, res)) return | ||
97 | if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return | ||
98 | |||
99 | // Check if the user who did the request is able to delete the video | ||
100 | if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return | ||
101 | |||
102 | return next() | ||
103 | } | ||
104 | ] | ||
105 | |||
106 | // --------------------------------------------------------------------------- | ||
107 | |||
108 | export { | ||
109 | listVideoCommentThreadsValidator, | ||
110 | listVideoThreadCommentsValidator, | ||
111 | addVideoCommentThreadValidator, | ||
112 | addVideoCommentReplyValidator, | ||
113 | videoCommentGetValidator, | ||
114 | removeVideoCommentValidator | ||
115 | } | ||
116 | |||
117 | // --------------------------------------------------------------------------- | ||
118 | |||
119 | async function isVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) { | ||
120 | const videoComment = await VideoCommentModel.loadById(id) | ||
121 | |||
122 | if (!videoComment) { | ||
123 | res.status(404) | ||
124 | .json({ error: 'Video comment thread not found' }) | ||
125 | .end() | ||
126 | |||
127 | return false | ||
128 | } | ||
129 | |||
130 | if (videoComment.videoId !== video.id) { | ||
131 | res.status(400) | ||
132 | .json({ error: 'Video comment is associated to this video.' }) | ||
133 | .end() | ||
134 | |||
135 | return false | ||
136 | } | ||
137 | |||
138 | if (videoComment.inReplyToCommentId !== null) { | ||
139 | res.status(400) | ||
140 | .json({ error: 'Video comment is not a thread.' }) | ||
141 | .end() | ||
142 | |||
143 | return false | ||
144 | } | ||
145 | |||
146 | res.locals.videoCommentThread = videoComment | ||
147 | return true | ||
148 | } | ||
149 | |||
150 | async function isVideoCommentExist (id: number, video: VideoModel, res: express.Response) { | ||
151 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) | ||
152 | |||
153 | if (!videoComment) { | ||
154 | res.status(404) | ||
155 | .json({ error: 'Video comment thread not found' }) | ||
156 | .end() | ||
157 | |||
158 | return false | ||
159 | } | ||
160 | |||
161 | if (videoComment.videoId !== video.id) { | ||
162 | res.status(400) | ||
163 | .json({ error: 'Video comment is associated to this video.' }) | ||
164 | .end() | ||
165 | |||
166 | return false | ||
167 | } | ||
168 | |||
169 | res.locals.videoComment = videoComment | ||
170 | return true | ||
171 | } | ||
172 | |||
173 | function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { | ||
174 | if (video.commentsEnabled !== true) { | ||
175 | res.status(409) | ||
176 | .json({ error: 'Video comments are disabled for this video.' }) | ||
177 | .end() | ||
178 | |||
179 | return false | ||
180 | } | ||
181 | |||
182 | return true | ||
183 | } | ||
184 | |||
185 | function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCommentModel, res: express.Response) { | ||
186 | const account = videoComment.Account | ||
187 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { | ||
188 | res.status(403) | ||
189 | .json({ error: 'Cannot remove video comment of another user' }) | ||
190 | .end() | ||
191 | return false | ||
192 | } | ||
193 | |||
194 | return true | ||
195 | } | ||
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts new file mode 100644 index 000000000..48d20f904 --- /dev/null +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -0,0 +1,75 @@ | |||
1 | import * as express from 'express' | ||
2 | import { body } from 'express-validator/check' | ||
3 | import { isIdValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { logger } from '../../../helpers/logger' | ||
5 | import { areValidationErrors } from '../utils' | ||
6 | import { getCommonVideoAttributes } from './videos' | ||
7 | import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' | ||
8 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | ||
9 | import { isVideoChannelOfAccountExist, isVideoMagnetUriValid, isVideoNameValid } from '../../../helpers/custom-validators/videos' | ||
10 | import { CONFIG } from '../../../initializers/constants' | ||
11 | import { CONSTRAINTS_FIELDS } from '../../../initializers' | ||
12 | |||
13 | const videoImportAddValidator = getCommonVideoAttributes().concat([ | ||
14 | body('channelId') | ||
15 | .toInt() | ||
16 | .custom(isIdValid).withMessage('Should have correct video channel id'), | ||
17 | body('targetUrl') | ||
18 | .optional() | ||
19 | .custom(isVideoImportTargetUrlValid).withMessage('Should have a valid video import target URL'), | ||
20 | body('magnetUri') | ||
21 | .optional() | ||
22 | .custom(isVideoMagnetUriValid).withMessage('Should have a valid video magnet URI'), | ||
23 | body('torrentfile') | ||
24 | .custom((value, { req }) => isVideoImportTorrentFile(req.files)).withMessage( | ||
25 | 'This torrent file is not supported or too large. Please, make sure it is of the following type: ' | ||
26 | + CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.EXTNAME.join(', ') | ||
27 | ), | ||
28 | body('name') | ||
29 | .optional() | ||
30 | .custom(isVideoNameValid).withMessage('Should have a valid name'), | ||
31 | |||
32 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
33 | logger.debug('Checking videoImportAddValidator parameters', { parameters: req.body }) | ||
34 | |||
35 | const user = res.locals.oauth.token.User | ||
36 | const torrentFile = req.files && req.files['torrentfile'] ? req.files['torrentfile'][0] : undefined | ||
37 | |||
38 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | ||
39 | |||
40 | if (req.body.targetUrl && CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true) { | ||
41 | cleanUpReqFiles(req) | ||
42 | return res.status(409) | ||
43 | .json({ error: 'HTTP import is not enabled on this instance.' }) | ||
44 | .end() | ||
45 | } | ||
46 | |||
47 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | ||
48 | cleanUpReqFiles(req) | ||
49 | return res.status(409) | ||
50 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) | ||
51 | .end() | ||
52 | } | ||
53 | |||
54 | if (!await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | ||
55 | |||
56 | // Check we have at least 1 required param | ||
57 | if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { | ||
58 | cleanUpReqFiles(req) | ||
59 | |||
60 | return res.status(400) | ||
61 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | ||
62 | .end() | ||
63 | } | ||
64 | |||
65 | return next() | ||
66 | } | ||
67 | ]) | ||
68 | |||
69 | // --------------------------------------------------------------------------- | ||
70 | |||
71 | export { | ||
72 | videoImportAddValidator | ||
73 | } | ||
74 | |||
75 | // --------------------------------------------------------------------------- | ||
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts new file mode 100644 index 000000000..bca64662f --- /dev/null +++ b/server/middlewares/validators/videos/video-watch.ts | |||
@@ -0,0 +1,28 @@ | |||
1 | import { body, param } from 'express-validator/check' | ||
2 | import * as express from 'express' | ||
3 | import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { isVideoExist } from '../../../helpers/custom-validators/videos' | ||
5 | import { areValidationErrors } from '../utils' | ||
6 | import { logger } from '../../../helpers/logger' | ||
7 | |||
8 | const videoWatchingValidator = [ | ||
9 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
10 | body('currentTime') | ||
11 | .toInt() | ||
12 | .isInt().withMessage('Should have correct current time'), | ||
13 | |||
14 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
15 | logger.debug('Checking videoWatching parameters', { parameters: req.body }) | ||
16 | |||
17 | if (areValidationErrors(req, res)) return | ||
18 | if (!await isVideoExist(req.params.videoId, res, 'id')) return | ||
19 | |||
20 | return next() | ||
21 | } | ||
22 | ] | ||
23 | |||
24 | // --------------------------------------------------------------------------- | ||
25 | |||
26 | export { | ||
27 | videoWatchingValidator | ||
28 | } | ||
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts new file mode 100644 index 000000000..d6b8aa725 --- /dev/null +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -0,0 +1,399 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'express-validator' | ||
3 | import { body, param, ValidationChain } from 'express-validator/check' | ||
4 | import { UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' | ||
5 | import { | ||
6 | isBooleanValid, | ||
7 | isDateValid, | ||
8 | isIdOrUUIDValid, | ||
9 | isIdValid, | ||
10 | isUUIDValid, | ||
11 | toIntOrNull, | ||
12 | toValueOrNull | ||
13 | } from '../../../helpers/custom-validators/misc' | ||
14 | import { | ||
15 | checkUserCanManageVideo, | ||
16 | isScheduleVideoUpdatePrivacyValid, | ||
17 | isVideoCategoryValid, | ||
18 | isVideoChannelOfAccountExist, | ||
19 | isVideoDescriptionValid, | ||
20 | isVideoExist, | ||
21 | isVideoFile, | ||
22 | isVideoImage, | ||
23 | isVideoLanguageValid, | ||
24 | isVideoLicenceValid, | ||
25 | isVideoNameValid, | ||
26 | isVideoPrivacyValid, | ||
27 | isVideoRatingTypeValid, | ||
28 | isVideoSupportValid, | ||
29 | isVideoTagsValid | ||
30 | } from '../../../helpers/custom-validators/videos' | ||
31 | import { getDurationFromVideoFile } from '../../../helpers/ffmpeg-utils' | ||
32 | import { logger } from '../../../helpers/logger' | ||
33 | import { CONSTRAINTS_FIELDS } from '../../../initializers' | ||
34 | import { VideoShareModel } from '../../../models/video/video-share' | ||
35 | import { authenticate } from '../../oauth' | ||
36 | import { areValidationErrors } from '../utils' | ||
37 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | ||
38 | import { VideoModel } from '../../../models/video/video' | ||
39 | import { UserModel } from '../../../models/account/user' | ||
40 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' | ||
41 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | ||
42 | import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership' | ||
43 | import { AccountModel } from '../../../models/account/account' | ||
44 | import { VideoFetchType } from '../../../helpers/video' | ||
45 | |||
46 | const videosAddValidator = getCommonVideoAttributes().concat([ | ||
47 | body('videofile') | ||
48 | .custom((value, { req }) => isVideoFile(req.files)).withMessage( | ||
49 | 'This file is not supported or too large. Please, make sure it is of the following type: ' | ||
50 | + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') | ||
51 | ), | ||
52 | body('name').custom(isVideoNameValid).withMessage('Should have a valid name'), | ||
53 | body('channelId') | ||
54 | .toInt() | ||
55 | .custom(isIdValid).withMessage('Should have correct video channel id'), | ||
56 | |||
57 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
58 | logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) | ||
59 | |||
60 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | ||
61 | if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req) | ||
62 | |||
63 | const videoFile: Express.Multer.File = req.files['videofile'][0] | ||
64 | const user = res.locals.oauth.token.User | ||
65 | |||
66 | if (!await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | ||
67 | |||
68 | const isAble = await user.isAbleToUploadVideo(videoFile) | ||
69 | if (isAble === false) { | ||
70 | res.status(403) | ||
71 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
72 | .end() | ||
73 | |||
74 | return cleanUpReqFiles(req) | ||
75 | } | ||
76 | |||
77 | let duration: number | ||
78 | |||
79 | try { | ||
80 | duration = await getDurationFromVideoFile(videoFile.path) | ||
81 | } catch (err) { | ||
82 | logger.error('Invalid input file in videosAddValidator.', { err }) | ||
83 | res.status(400) | ||
84 | .json({ error: 'Invalid input file.' }) | ||
85 | .end() | ||
86 | |||
87 | return cleanUpReqFiles(req) | ||
88 | } | ||
89 | |||
90 | videoFile['duration'] = duration | ||
91 | |||
92 | return next() | ||
93 | } | ||
94 | ]) | ||
95 | |||
96 | const videosUpdateValidator = getCommonVideoAttributes().concat([ | ||
97 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
98 | body('name') | ||
99 | .optional() | ||
100 | .custom(isVideoNameValid).withMessage('Should have a valid name'), | ||
101 | body('channelId') | ||
102 | .optional() | ||
103 | .toInt() | ||
104 | .custom(isIdValid).withMessage('Should have correct video channel id'), | ||
105 | |||
106 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
107 | logger.debug('Checking videosUpdate parameters', { parameters: req.body }) | ||
108 | |||
109 | if (areValidationErrors(req, res)) return cleanUpReqFiles(req) | ||
110 | if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req) | ||
111 | if (!await isVideoExist(req.params.id, res)) return cleanUpReqFiles(req) | ||
112 | |||
113 | const video = res.locals.video | ||
114 | |||
115 | // Check if the user who did the request is able to update the video | ||
116 | const user = res.locals.oauth.token.User | ||
117 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) | ||
118 | |||
119 | if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { | ||
120 | cleanUpReqFiles(req) | ||
121 | return res.status(409) | ||
122 | .json({ error: 'Cannot set "private" a video that was not private.' }) | ||
123 | .end() | ||
124 | } | ||
125 | |||
126 | if (req.body.channelId && !await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | ||
127 | |||
128 | return next() | ||
129 | } | ||
130 | ]) | ||
131 | |||
132 | const videosCustomGetValidator = (fetchType: VideoFetchType) => { | ||
133 | return [ | ||
134 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
135 | |||
136 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
137 | logger.debug('Checking videosGet parameters', { parameters: req.params }) | ||
138 | |||
139 | if (areValidationErrors(req, res)) return | ||
140 | if (!await isVideoExist(req.params.id, res, fetchType)) return | ||
141 | |||
142 | const video: VideoModel = res.locals.video | ||
143 | |||
144 | // Video private or blacklisted | ||
145 | if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { | ||
146 | return authenticate(req, res, () => { | ||
147 | const user: UserModel = res.locals.oauth.token.User | ||
148 | |||
149 | // Only the owner or a user that have blacklist rights can see the video | ||
150 | if (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) { | ||
151 | return res.status(403) | ||
152 | .json({ error: 'Cannot get this private or blacklisted video.' }) | ||
153 | .end() | ||
154 | } | ||
155 | |||
156 | return next() | ||
157 | }) | ||
158 | } | ||
159 | |||
160 | // Video is public, anyone can access it | ||
161 | if (video.privacy === VideoPrivacy.PUBLIC) return next() | ||
162 | |||
163 | // Video is unlisted, check we used the uuid to fetch it | ||
164 | if (video.privacy === VideoPrivacy.UNLISTED) { | ||
165 | if (isUUIDValid(req.params.id)) return next() | ||
166 | |||
167 | // Don't leak this unlisted video | ||
168 | return res.status(404).end() | ||
169 | } | ||
170 | } | ||
171 | ] | ||
172 | } | ||
173 | |||
174 | const videosGetValidator = videosCustomGetValidator('all') | ||
175 | |||
176 | const videosRemoveValidator = [ | ||
177 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
178 | |||
179 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
180 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) | ||
181 | |||
182 | if (areValidationErrors(req, res)) return | ||
183 | if (!await isVideoExist(req.params.id, res)) return | ||
184 | |||
185 | // Check if the user who did the request is able to delete the video | ||
186 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return | ||
187 | |||
188 | return next() | ||
189 | } | ||
190 | ] | ||
191 | |||
192 | const videoRateValidator = [ | ||
193 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
194 | body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), | ||
195 | |||
196 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
197 | logger.debug('Checking videoRate parameters', { parameters: req.body }) | ||
198 | |||
199 | if (areValidationErrors(req, res)) return | ||
200 | if (!await isVideoExist(req.params.id, res)) return | ||
201 | |||
202 | return next() | ||
203 | } | ||
204 | ] | ||
205 | |||
206 | const videosShareValidator = [ | ||
207 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
208 | param('accountId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid account id'), | ||
209 | |||
210 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
211 | logger.debug('Checking videoShare parameters', { parameters: req.params }) | ||
212 | |||
213 | if (areValidationErrors(req, res)) return | ||
214 | if (!await isVideoExist(req.params.id, res)) return | ||
215 | |||
216 | const share = await VideoShareModel.load(req.params.accountId, res.locals.video.id, undefined) | ||
217 | if (!share) { | ||
218 | return res.status(404) | ||
219 | .end() | ||
220 | } | ||
221 | |||
222 | res.locals.videoShare = share | ||
223 | return next() | ||
224 | } | ||
225 | ] | ||
226 | |||
227 | const videosChangeOwnershipValidator = [ | ||
228 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
229 | |||
230 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
231 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
232 | |||
233 | if (areValidationErrors(req, res)) return | ||
234 | if (!await isVideoExist(req.params.videoId, res)) return | ||
235 | |||
236 | // Check if the user who did the request is able to change the ownership of the video | ||
237 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return | ||
238 | |||
239 | const nextOwner = await AccountModel.loadLocalByName(req.body.username) | ||
240 | if (!nextOwner) { | ||
241 | res.status(400) | ||
242 | .type('json') | ||
243 | .end() | ||
244 | return | ||
245 | } | ||
246 | res.locals.nextOwner = nextOwner | ||
247 | |||
248 | return next() | ||
249 | } | ||
250 | ] | ||
251 | |||
252 | const videosTerminateChangeOwnershipValidator = [ | ||
253 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
254 | |||
255 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
256 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
257 | |||
258 | if (areValidationErrors(req, res)) return | ||
259 | if (!await doesChangeVideoOwnershipExist(req.params.id, res)) return | ||
260 | |||
261 | // Check if the user who did the request is able to change the ownership of the video | ||
262 | if (!checkUserCanTerminateOwnershipChange(res.locals.oauth.token.User, res.locals.videoChangeOwnership, res)) return | ||
263 | |||
264 | return next() | ||
265 | }, | ||
266 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
267 | const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel | ||
268 | |||
269 | if (videoChangeOwnership.status === VideoChangeOwnershipStatus.WAITING) { | ||
270 | return next() | ||
271 | } else { | ||
272 | res.status(403) | ||
273 | .json({ error: 'Ownership already accepted or refused' }) | ||
274 | .end() | ||
275 | return | ||
276 | } | ||
277 | } | ||
278 | ] | ||
279 | |||
280 | const videosAcceptChangeOwnershipValidator = [ | ||
281 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
282 | const body = req.body as VideoChangeOwnershipAccept | ||
283 | if (!await isVideoChannelOfAccountExist(body.channelId, res.locals.oauth.token.User, res)) return | ||
284 | |||
285 | const user = res.locals.oauth.token.User | ||
286 | const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel | ||
287 | const isAble = await user.isAbleToUploadVideo(videoChangeOwnership.Video.getOriginalFile()) | ||
288 | if (isAble === false) { | ||
289 | res.status(403) | ||
290 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
291 | .end() | ||
292 | return | ||
293 | } | ||
294 | |||
295 | return next() | ||
296 | } | ||
297 | ] | ||
298 | |||
299 | function getCommonVideoAttributes () { | ||
300 | return [ | ||
301 | body('thumbnailfile') | ||
302 | .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( | ||
303 | 'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' | ||
304 | + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') | ||
305 | ), | ||
306 | body('previewfile') | ||
307 | .custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( | ||
308 | 'This preview file is not supported or too large. Please, make sure it is of the following type: ' | ||
309 | + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') | ||
310 | ), | ||
311 | |||
312 | body('category') | ||
313 | .optional() | ||
314 | .customSanitizer(toIntOrNull) | ||
315 | .custom(isVideoCategoryValid).withMessage('Should have a valid category'), | ||
316 | body('licence') | ||
317 | .optional() | ||
318 | .customSanitizer(toIntOrNull) | ||
319 | .custom(isVideoLicenceValid).withMessage('Should have a valid licence'), | ||
320 | body('language') | ||
321 | .optional() | ||
322 | .customSanitizer(toValueOrNull) | ||
323 | .custom(isVideoLanguageValid).withMessage('Should have a valid language'), | ||
324 | body('nsfw') | ||
325 | .optional() | ||
326 | .toBoolean() | ||
327 | .custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), | ||
328 | body('waitTranscoding') | ||
329 | .optional() | ||
330 | .toBoolean() | ||
331 | .custom(isBooleanValid).withMessage('Should have a valid wait transcoding attribute'), | ||
332 | body('privacy') | ||
333 | .optional() | ||
334 | .toInt() | ||
335 | .custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), | ||
336 | body('description') | ||
337 | .optional() | ||
338 | .customSanitizer(toValueOrNull) | ||
339 | .custom(isVideoDescriptionValid).withMessage('Should have a valid description'), | ||
340 | body('support') | ||
341 | .optional() | ||
342 | .customSanitizer(toValueOrNull) | ||
343 | .custom(isVideoSupportValid).withMessage('Should have a valid support text'), | ||
344 | body('tags') | ||
345 | .optional() | ||
346 | .customSanitizer(toValueOrNull) | ||
347 | .custom(isVideoTagsValid).withMessage('Should have correct tags'), | ||
348 | body('commentsEnabled') | ||
349 | .optional() | ||
350 | .toBoolean() | ||
351 | .custom(isBooleanValid).withMessage('Should have comments enabled boolean'), | ||
352 | |||
353 | body('scheduleUpdate') | ||
354 | .optional() | ||
355 | .customSanitizer(toValueOrNull), | ||
356 | body('scheduleUpdate.updateAt') | ||
357 | .optional() | ||
358 | .custom(isDateValid).withMessage('Should have a valid schedule update date'), | ||
359 | body('scheduleUpdate.privacy') | ||
360 | .optional() | ||
361 | .toInt() | ||
362 | .custom(isScheduleVideoUpdatePrivacyValid).withMessage('Should have correct schedule update privacy') | ||
363 | ] as (ValidationChain | express.Handler)[] | ||
364 | } | ||
365 | |||
366 | // --------------------------------------------------------------------------- | ||
367 | |||
368 | export { | ||
369 | videosAddValidator, | ||
370 | videosUpdateValidator, | ||
371 | videosGetValidator, | ||
372 | videosCustomGetValidator, | ||
373 | videosRemoveValidator, | ||
374 | videosShareValidator, | ||
375 | |||
376 | videoRateValidator, | ||
377 | |||
378 | videosChangeOwnershipValidator, | ||
379 | videosTerminateChangeOwnershipValidator, | ||
380 | videosAcceptChangeOwnershipValidator, | ||
381 | |||
382 | getCommonVideoAttributes | ||
383 | } | ||
384 | |||
385 | // --------------------------------------------------------------------------- | ||
386 | |||
387 | function areErrorsInScheduleUpdate (req: express.Request, res: express.Response) { | ||
388 | if (req.body.scheduleUpdate) { | ||
389 | if (!req.body.scheduleUpdate.updateAt) { | ||
390 | res.status(400) | ||
391 | .json({ error: 'Schedule update at is mandatory.' }) | ||
392 | .end() | ||
393 | |||
394 | return true | ||
395 | } | ||
396 | } | ||
397 | |||
398 | return false | ||
399 | } | ||