diff options
Diffstat (limited to 'server/middlewares/validators/videos')
13 files changed, 475 insertions, 337 deletions
diff --git a/server/middlewares/validators/videos/index.ts b/server/middlewares/validators/videos/index.ts index 1eabada0a..369c2c9b6 100644 --- a/server/middlewares/validators/videos/index.ts +++ b/server/middlewares/validators/videos/index.ts | |||
@@ -3,6 +3,8 @@ export * from './video-captions' | |||
3 | export * from './video-channels' | 3 | export * from './video-channels' |
4 | export * from './video-comments' | 4 | export * from './video-comments' |
5 | export * from './video-imports' | 5 | export * from './video-imports' |
6 | export * from './video-live' | ||
7 | export * from './video-ownership-changes' | ||
6 | export * from './video-watch' | 8 | export * from './video-watch' |
7 | export * from './video-rates' | 9 | export * from './video-rates' |
8 | export * from './video-shares' | 10 | export * from './video-shares' |
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts index 88c788a43..21141d84d 100644 --- a/server/middlewares/validators/videos/video-blacklist.ts +++ b/server/middlewares/validators/videos/video-blacklist.ts | |||
@@ -1,14 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, query } from 'express-validator' |
3 | import { isBooleanValid, isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' | 3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
4 | import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' | ||
4 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist' | 5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist' |
5 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
6 | import { doesVideoBlacklistExist, doesVideoExist } from '../../../helpers/middlewares' | 7 | import { areValidationErrors, doesVideoBlacklistExist, doesVideoExist, isValidVideoIdParam } from '../shared' |
7 | import { areValidationErrors } from '../utils' | ||
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
9 | 8 | ||
10 | const videosBlacklistRemoveValidator = [ | 9 | const videosBlacklistRemoveValidator = [ |
11 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 10 | isValidVideoIdParam('videoId'), |
12 | 11 | ||
13 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 12 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
14 | logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) | 13 | logger.debug('Checking blacklistRemove parameters.', { parameters: req.params }) |
@@ -22,7 +21,8 @@ const videosBlacklistRemoveValidator = [ | |||
22 | ] | 21 | ] |
23 | 22 | ||
24 | const videosBlacklistAddValidator = [ | 23 | const videosBlacklistAddValidator = [ |
25 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 24 | isValidVideoIdParam('videoId'), |
25 | |||
26 | body('unfederate') | 26 | body('unfederate') |
27 | .optional() | 27 | .optional() |
28 | .customSanitizer(toBooleanOrNull) | 28 | .customSanitizer(toBooleanOrNull) |
@@ -39,10 +39,10 @@ const videosBlacklistAddValidator = [ | |||
39 | 39 | ||
40 | const video = res.locals.videoAll | 40 | const video = res.locals.videoAll |
41 | if (req.body.unfederate === true && video.remote === true) { | 41 | if (req.body.unfederate === true && video.remote === true) { |
42 | return res | 42 | return res.fail({ |
43 | .status(HttpStatusCode.CONFLICT_409) | 43 | status: HttpStatusCode.CONFLICT_409, |
44 | .send({ error: 'You cannot unfederate a remote video.' }) | 44 | message: 'You cannot unfederate a remote video.' |
45 | .end() | 45 | }) |
46 | } | 46 | } |
47 | 47 | ||
48 | return next() | 48 | return next() |
@@ -50,7 +50,8 @@ const videosBlacklistAddValidator = [ | |||
50 | ] | 50 | ] |
51 | 51 | ||
52 | const videosBlacklistUpdateValidator = [ | 52 | const videosBlacklistUpdateValidator = [ |
53 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 53 | isValidVideoIdParam('videoId'), |
54 | |||
54 | body('reason') | 55 | body('reason') |
55 | .optional() | 56 | .optional() |
56 | .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), | 57 | .custom(isVideoBlacklistReasonValid).withMessage('Should have a valid reason'), |
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts index 872d9c2ab..2946f3e15 100644 --- a/server/middlewares/validators/videos/video-captions.ts +++ b/server/middlewares/validators/videos/video-captions.ts | |||
@@ -1,17 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { areValidationErrors } from '../utils' | ||
3 | import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { body, param } from 'express-validator' | 2 | import { body, param } from 'express-validator' |
5 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants' | ||
6 | import { UserRight } from '../../../../shared' | 3 | import { UserRight } from '../../../../shared' |
7 | import { logger } from '../../../helpers/logger' | ||
8 | import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' | 4 | import { isVideoCaptionFile, isVideoCaptionLanguageValid } from '../../../helpers/custom-validators/video-captions' |
9 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 5 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
10 | import { checkUserCanManageVideo, doesVideoCaptionExist, doesVideoExist } from '../../../helpers/middlewares' | 6 | import { logger } from '../../../helpers/logger' |
7 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../../initializers/constants' | ||
8 | import { areValidationErrors, checkUserCanManageVideo, doesVideoCaptionExist, doesVideoExist, isValidVideoIdParam } from '../shared' | ||
11 | 9 | ||
12 | const addVideoCaptionValidator = [ | 10 | const addVideoCaptionValidator = [ |
13 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | 11 | isValidVideoIdParam('videoId'), |
14 | param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | 12 | |
13 | param('captionLanguage') | ||
14 | .custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | ||
15 | |||
15 | body('captionfile') | 16 | body('captionfile') |
16 | .custom((_, { req }) => isVideoCaptionFile(req.files, 'captionfile')) | 17 | .custom((_, { req }) => isVideoCaptionFile(req.files, 'captionfile')) |
17 | .withMessage( | 18 | .withMessage( |
@@ -35,8 +36,10 @@ const addVideoCaptionValidator = [ | |||
35 | ] | 36 | ] |
36 | 37 | ||
37 | const deleteVideoCaptionValidator = [ | 38 | const deleteVideoCaptionValidator = [ |
38 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | 39 | isValidVideoIdParam('videoId'), |
39 | param('captionLanguage').custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | 40 | |
41 | param('captionLanguage') | ||
42 | .custom(isVideoCaptionLanguageValid).not().isEmpty().withMessage('Should have a valid caption language'), | ||
40 | 43 | ||
41 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 44 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
42 | logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params }) | 45 | logger.debug('Checking deleteVideoCaption parameters', { parameters: req.params }) |
@@ -54,7 +57,7 @@ const deleteVideoCaptionValidator = [ | |||
54 | ] | 57 | ] |
55 | 58 | ||
56 | const listVideoCaptionsValidator = [ | 59 | const listVideoCaptionsValidator = [ |
57 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid video id'), | 60 | isValidVideoIdParam('videoId'), |
58 | 61 | ||
59 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 62 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
60 | logger.debug('Checking listVideoCaptions parameters', { parameters: req.params }) | 63 | logger.debug('Checking listVideoCaptions parameters', { parameters: req.params }) |
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 2463d281c..e7df185e4 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -3,6 +3,7 @@ import { body, param, query } from 'express-validator' | |||
3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' | 3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' |
4 | import { MChannelAccountDefault, MUser } from '@server/types/models' | 4 | import { MChannelAccountDefault, MUser } from '@server/types/models' |
5 | import { UserRight } from '../../../../shared' | 5 | import { UserRight } from '../../../../shared' |
6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
6 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | 7 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' |
7 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | 8 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' |
8 | import { | 9 | import { |
@@ -11,11 +12,9 @@ import { | |||
11 | isVideoChannelSupportValid | 12 | isVideoChannelSupportValid |
12 | } from '../../../helpers/custom-validators/video-channels' | 13 | } from '../../../helpers/custom-validators/video-channels' |
13 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
14 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' | 15 | import { ActorModel } from '../../../models/actor/actor' |
15 | import { ActorModel } from '../../../models/activitypub/actor' | ||
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | 16 | import { VideoChannelModel } from '../../../models/video/video-channel' |
17 | import { areValidationErrors } from '../utils' | 17 | import { areValidationErrors, doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../shared' |
18 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
19 | 18 | ||
20 | const videoChannelsAddValidator = [ | 19 | const videoChannelsAddValidator = [ |
21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | 20 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), |
@@ -30,17 +29,16 @@ const videoChannelsAddValidator = [ | |||
30 | 29 | ||
31 | const actor = await ActorModel.loadLocalByName(req.body.name) | 30 | const actor = await ActorModel.loadLocalByName(req.body.name) |
32 | if (actor) { | 31 | if (actor) { |
33 | res.status(HttpStatusCode.CONFLICT_409) | 32 | res.fail({ |
34 | .send({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' }) | 33 | status: HttpStatusCode.CONFLICT_409, |
35 | .end() | 34 | message: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' |
35 | }) | ||
36 | return false | 36 | return false |
37 | } | 37 | } |
38 | 38 | ||
39 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) | 39 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) |
40 | if (count >= VIDEO_CHANNELS.MAX_PER_USER) { | 40 | if (count >= VIDEO_CHANNELS.MAX_PER_USER) { |
41 | res.status(HttpStatusCode.BAD_REQUEST_400) | 41 | res.fail({ message: `You cannot create more than ${VIDEO_CHANNELS.MAX_PER_USER} channels` }) |
42 | .send({ error: `You cannot create more than ${VIDEO_CHANNELS.MAX_PER_USER} channels` }) | ||
43 | .end() | ||
44 | return false | 42 | return false |
45 | } | 43 | } |
46 | 44 | ||
@@ -71,13 +69,17 @@ const videoChannelsUpdateValidator = [ | |||
71 | 69 | ||
72 | // We need to make additional checks | 70 | // We need to make additional checks |
73 | if (res.locals.videoChannel.Actor.isOwned() === false) { | 71 | if (res.locals.videoChannel.Actor.isOwned() === false) { |
74 | return res.status(HttpStatusCode.FORBIDDEN_403) | 72 | return res.fail({ |
75 | .json({ error: 'Cannot update video channel of another server' }) | 73 | status: HttpStatusCode.FORBIDDEN_403, |
74 | message: 'Cannot update video channel of another server' | ||
75 | }) | ||
76 | } | 76 | } |
77 | 77 | ||
78 | if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { | 78 | if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { |
79 | return res.status(HttpStatusCode.FORBIDDEN_403) | 79 | return res.fail({ |
80 | .json({ error: 'Cannot update video channel of another user' }) | 80 | status: HttpStatusCode.FORBIDDEN_403, |
81 | message: 'Cannot update video channel of another user' | ||
82 | }) | ||
81 | } | 83 | } |
82 | 84 | ||
83 | return next() | 85 | return next() |
@@ -139,6 +141,18 @@ const videoChannelStatsValidator = [ | |||
139 | } | 141 | } |
140 | ] | 142 | ] |
141 | 143 | ||
144 | const videoChannelsListValidator = [ | ||
145 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), | ||
146 | |||
147 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
148 | logger.debug('Checking video channels search query', { parameters: req.query }) | ||
149 | |||
150 | if (areValidationErrors(req, res)) return | ||
151 | |||
152 | return next() | ||
153 | } | ||
154 | ] | ||
155 | |||
142 | // --------------------------------------------------------------------------- | 156 | // --------------------------------------------------------------------------- |
143 | 157 | ||
144 | export { | 158 | export { |
@@ -146,6 +160,7 @@ export { | |||
146 | videoChannelsUpdateValidator, | 160 | videoChannelsUpdateValidator, |
147 | videoChannelsRemoveValidator, | 161 | videoChannelsRemoveValidator, |
148 | videoChannelsNameWithHostValidator, | 162 | videoChannelsNameWithHostValidator, |
163 | videoChannelsListValidator, | ||
149 | localVideoChannelValidator, | 164 | localVideoChannelValidator, |
150 | videoChannelStatsValidator | 165 | videoChannelStatsValidator |
151 | } | 166 | } |
@@ -154,10 +169,10 @@ export { | |||
154 | 169 | ||
155 | function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { | 170 | function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { |
156 | if (videoChannel.Actor.isOwned() === false) { | 171 | if (videoChannel.Actor.isOwned() === false) { |
157 | res.status(HttpStatusCode.FORBIDDEN_403) | 172 | res.fail({ |
158 | .json({ error: 'Cannot remove video channel of another server.' }) | 173 | status: HttpStatusCode.FORBIDDEN_403, |
159 | .end() | 174 | message: 'Cannot remove video channel of another server.' |
160 | 175 | }) | |
161 | return false | 176 | return false |
162 | } | 177 | } |
163 | 178 | ||
@@ -165,10 +180,10 @@ function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAcco | |||
165 | // The user can delete it if s/he is an admin | 180 | // The user can delete it if s/he is an admin |
166 | // Or if s/he is the video channel's account | 181 | // Or if s/he is the video channel's account |
167 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) { | 182 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) { |
168 | res.status(HttpStatusCode.FORBIDDEN_403) | 183 | res.fail({ |
169 | .json({ error: 'Cannot remove video channel of another user' }) | 184 | status: HttpStatusCode.FORBIDDEN_403, |
170 | .end() | 185 | message: 'Cannot remove video channel of another user' |
171 | 186 | }) | |
172 | return false | 187 | return false |
173 | } | 188 | } |
174 | 189 | ||
@@ -179,10 +194,10 @@ async function checkVideoChannelIsNotTheLastOne (res: express.Response) { | |||
179 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) | 194 | const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) |
180 | 195 | ||
181 | if (count <= 1) { | 196 | if (count <= 1) { |
182 | res.status(HttpStatusCode.CONFLICT_409) | 197 | res.fail({ |
183 | .json({ error: 'Cannot remove the last channel of this user' }) | 198 | status: HttpStatusCode.CONFLICT_409, |
184 | .end() | 199 | message: 'Cannot remove the last channel of this user' |
185 | 200 | }) | |
186 | return false | 201 | return false |
187 | } | 202 | } |
188 | 203 | ||
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts index 1afacfed8..885506ebe 100644 --- a/server/middlewares/validators/videos/video-comments.ts +++ b/server/middlewares/validators/videos/video-comments.ts | |||
@@ -2,19 +2,14 @@ import * as express from 'express' | |||
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { MUserAccountUrl } from '@server/types/models' | 3 | import { MUserAccountUrl } from '@server/types/models' |
4 | import { UserRight } from '../../../../shared' | 4 | import { UserRight } from '../../../../shared' |
5 | import { exists, isBooleanValid, isIdOrUUIDValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | 5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
6 | import { | 6 | import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' |
7 | doesVideoCommentExist, | 7 | import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' |
8 | doesVideoCommentThreadExist, | ||
9 | isValidVideoCommentText | ||
10 | } from '../../../helpers/custom-validators/video-comments' | ||
11 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
12 | import { doesVideoExist } from '../../../helpers/middlewares' | ||
13 | import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation' | 9 | import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation' |
14 | import { Hooks } from '../../../lib/plugins/hooks' | 10 | import { Hooks } from '../../../lib/plugins/hooks' |
15 | import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video' | 11 | import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video' |
16 | import { areValidationErrors } from '../utils' | 12 | import { areValidationErrors, doesVideoCommentExist, doesVideoCommentThreadExist, doesVideoExist, isValidVideoIdParam } from '../shared' |
17 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
18 | 13 | ||
19 | const listVideoCommentsValidator = [ | 14 | const listVideoCommentsValidator = [ |
20 | query('isLocal') | 15 | query('isLocal') |
@@ -45,7 +40,7 @@ const listVideoCommentsValidator = [ | |||
45 | ] | 40 | ] |
46 | 41 | ||
47 | const listVideoCommentThreadsValidator = [ | 42 | const listVideoCommentThreadsValidator = [ |
48 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 43 | isValidVideoIdParam('videoId'), |
49 | 44 | ||
50 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 45 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
51 | logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params }) | 46 | logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params }) |
@@ -58,8 +53,10 @@ const listVideoCommentThreadsValidator = [ | |||
58 | ] | 53 | ] |
59 | 54 | ||
60 | const listVideoThreadCommentsValidator = [ | 55 | const listVideoThreadCommentsValidator = [ |
61 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 56 | isValidVideoIdParam('videoId'), |
62 | param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'), | 57 | |
58 | param('threadId') | ||
59 | .custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'), | ||
63 | 60 | ||
64 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 61 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
65 | logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params }) | 62 | logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params }) |
@@ -73,8 +70,10 @@ const listVideoThreadCommentsValidator = [ | |||
73 | ] | 70 | ] |
74 | 71 | ||
75 | const addVideoCommentThreadValidator = [ | 72 | const addVideoCommentThreadValidator = [ |
76 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 73 | isValidVideoIdParam('videoId'), |
77 | body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), | 74 | |
75 | body('text') | ||
76 | .custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), | ||
78 | 77 | ||
79 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 78 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
80 | logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body }) | 79 | logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params, body: req.body }) |
@@ -89,8 +88,10 @@ const addVideoCommentThreadValidator = [ | |||
89 | ] | 88 | ] |
90 | 89 | ||
91 | const addVideoCommentReplyValidator = [ | 90 | const addVideoCommentReplyValidator = [ |
92 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 91 | isValidVideoIdParam('videoId'), |
92 | |||
93 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | 93 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), |
94 | |||
94 | body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), | 95 | body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'), |
95 | 96 | ||
96 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 97 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
@@ -107,8 +108,10 @@ const addVideoCommentReplyValidator = [ | |||
107 | ] | 108 | ] |
108 | 109 | ||
109 | const videoCommentGetValidator = [ | 110 | const videoCommentGetValidator = [ |
110 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 111 | isValidVideoIdParam('videoId'), |
111 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | 112 | |
113 | param('commentId') | ||
114 | .custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | ||
112 | 115 | ||
113 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 116 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
114 | logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) | 117 | logger.debug('Checking videoCommentGetValidator parameters.', { parameters: req.params }) |
@@ -122,7 +125,8 @@ const videoCommentGetValidator = [ | |||
122 | ] | 125 | ] |
123 | 126 | ||
124 | const removeVideoCommentValidator = [ | 127 | const removeVideoCommentValidator = [ |
125 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 128 | isValidVideoIdParam('videoId'), |
129 | |||
126 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), | 130 | param('commentId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid commentId'), |
127 | 131 | ||
128 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 132 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
@@ -155,9 +159,10 @@ export { | |||
155 | 159 | ||
156 | function isVideoCommentsEnabled (video: MVideo, res: express.Response) { | 160 | function isVideoCommentsEnabled (video: MVideo, res: express.Response) { |
157 | if (video.commentsEnabled !== true) { | 161 | if (video.commentsEnabled !== true) { |
158 | res.status(HttpStatusCode.CONFLICT_409) | 162 | res.fail({ |
159 | .json({ error: 'Video comments are disabled for this video.' }) | 163 | status: HttpStatusCode.CONFLICT_409, |
160 | 164 | message: 'Video comments are disabled for this video.' | |
165 | }) | ||
161 | return false | 166 | return false |
162 | } | 167 | } |
163 | 168 | ||
@@ -166,9 +171,10 @@ function isVideoCommentsEnabled (video: MVideo, res: express.Response) { | |||
166 | 171 | ||
167 | function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) { | 172 | function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) { |
168 | if (videoComment.isDeleted()) { | 173 | if (videoComment.isDeleted()) { |
169 | res.status(HttpStatusCode.CONFLICT_409) | 174 | res.fail({ |
170 | .json({ error: 'This comment is already deleted' }) | 175 | status: HttpStatusCode.CONFLICT_409, |
171 | 176 | message: 'This comment is already deleted' | |
177 | }) | ||
172 | return false | 178 | return false |
173 | } | 179 | } |
174 | 180 | ||
@@ -179,9 +185,10 @@ function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MC | |||
179 | videoComment.accountId !== userAccount.id && // Not the comment owner | 185 | videoComment.accountId !== userAccount.id && // Not the comment owner |
180 | videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner | 186 | videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner |
181 | ) { | 187 | ) { |
182 | res.status(HttpStatusCode.FORBIDDEN_403) | 188 | res.fail({ |
183 | .json({ error: 'Cannot remove video comment of another user' }) | 189 | status: HttpStatusCode.FORBIDDEN_403, |
184 | 190 | message: 'Cannot remove video comment of another user' | |
191 | }) | ||
185 | return false | 192 | return false |
186 | } | 193 | } |
187 | 194 | ||
@@ -215,9 +222,11 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon | |||
215 | 222 | ||
216 | if (!acceptedResult || acceptedResult.accepted !== true) { | 223 | if (!acceptedResult || acceptedResult.accepted !== true) { |
217 | logger.info('Refused local comment.', { acceptedResult, acceptParameters }) | 224 | logger.info('Refused local comment.', { acceptedResult, acceptParameters }) |
218 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
219 | .json({ error: acceptedResult?.errorMessage || 'Refused local comment' }) | ||
220 | 225 | ||
226 | res.fail({ | ||
227 | status: HttpStatusCode.FORBIDDEN_403, | ||
228 | message: acceptedResult?.errorMessage || 'Refused local comment' | ||
229 | }) | ||
221 | return false | 230 | return false |
222 | } | 231 | } |
223 | 232 | ||
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts index c53af3861..85dc647ce 100644 --- a/server/middlewares/validators/videos/video-imports.ts +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -2,18 +2,17 @@ import * as express from 'express' | |||
2 | import { body } from 'express-validator' | 2 | import { body } from 'express-validator' |
3 | import { isPreImportVideoAccepted } from '@server/lib/moderation' | 3 | import { isPreImportVideoAccepted } from '@server/lib/moderation' |
4 | import { Hooks } from '@server/lib/plugins/hooks' | 4 | import { Hooks } from '@server/lib/plugins/hooks' |
5 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
5 | import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' | 6 | import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' |
6 | import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' | 7 | import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' |
7 | import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' | 8 | import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' |
8 | import { isVideoMagnetUriValid, isVideoNameValid } from '../../../helpers/custom-validators/videos' | 9 | import { isVideoMagnetUriValid, isVideoNameValid } from '../../../helpers/custom-validators/videos' |
9 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 10 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
10 | import { logger } from '../../../helpers/logger' | 11 | import { logger } from '../../../helpers/logger' |
11 | import { doesVideoChannelOfAccountExist } from '../../../helpers/middlewares' | ||
12 | import { CONFIG } from '../../../initializers/config' | 12 | import { CONFIG } from '../../../initializers/config' |
13 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' | 13 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' |
14 | import { areValidationErrors } from '../utils' | 14 | import { areValidationErrors, doesVideoChannelOfAccountExist } from '../shared' |
15 | import { getCommonVideoEditAttributes } from './videos' | 15 | import { getCommonVideoEditAttributes } from './videos' |
16 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
17 | 16 | ||
18 | const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | 17 | const videoImportAddValidator = getCommonVideoEditAttributes().concat([ |
19 | body('channelId') | 18 | body('channelId') |
@@ -33,7 +32,9 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
33 | ), | 32 | ), |
34 | body('name') | 33 | body('name') |
35 | .optional() | 34 | .optional() |
36 | .custom(isVideoNameValid).withMessage('Should have a valid name'), | 35 | .custom(isVideoNameValid).withMessage( |
36 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` | ||
37 | ), | ||
37 | 38 | ||
38 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 39 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
39 | logger.debug('Checking videoImportAddValidator parameters', { parameters: req.body }) | 40 | logger.debug('Checking videoImportAddValidator parameters', { parameters: req.body }) |
@@ -45,16 +46,20 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
45 | 46 | ||
46 | if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true && req.body.targetUrl) { | 47 | if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true && req.body.targetUrl) { |
47 | cleanUpReqFiles(req) | 48 | cleanUpReqFiles(req) |
48 | return res.status(HttpStatusCode.CONFLICT_409) | 49 | |
49 | .json({ error: 'HTTP import is not enabled on this instance.' }) | 50 | return res.fail({ |
50 | .end() | 51 | status: HttpStatusCode.CONFLICT_409, |
52 | message: 'HTTP import is not enabled on this instance.' | ||
53 | }) | ||
51 | } | 54 | } |
52 | 55 | ||
53 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | 56 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { |
54 | cleanUpReqFiles(req) | 57 | cleanUpReqFiles(req) |
55 | return res.status(HttpStatusCode.CONFLICT_409) | 58 | |
56 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) | 59 | return res.fail({ |
57 | .end() | 60 | status: HttpStatusCode.CONFLICT_409, |
61 | message: 'Torrent/magnet URI import is not enabled on this instance.' | ||
62 | }) | ||
58 | } | 63 | } |
59 | 64 | ||
60 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 65 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) |
@@ -63,9 +68,7 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
63 | if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { | 68 | if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { |
64 | cleanUpReqFiles(req) | 69 | cleanUpReqFiles(req) |
65 | 70 | ||
66 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 71 | return res.fail({ message: 'Should have a magnetUri or a targetUrl or a torrent file.' }) |
67 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | ||
68 | .end() | ||
69 | } | 72 | } |
70 | 73 | ||
71 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) | 74 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) |
@@ -101,9 +104,11 @@ async function isImportAccepted (req: express.Request, res: express.Response) { | |||
101 | 104 | ||
102 | if (!acceptedResult || acceptedResult.accepted !== true) { | 105 | if (!acceptedResult || acceptedResult.accepted !== true) { |
103 | logger.info('Refused to import video.', { acceptedResult, acceptParameters }) | 106 | logger.info('Refused to import video.', { acceptedResult, acceptParameters }) |
104 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
105 | .json({ error: acceptedResult.errorMessage || 'Refused to import video' }) | ||
106 | 107 | ||
108 | res.fail({ | ||
109 | status: HttpStatusCode.FORBIDDEN_403, | ||
110 | message: acceptedResult.errorMessage || 'Refused to import video' | ||
111 | }) | ||
107 | return false | 112 | return false |
108 | } | 113 | } |
109 | 114 | ||
diff --git a/server/middlewares/validators/videos/video-live.ts b/server/middlewares/validators/videos/video-live.ts index 3a73e1272..7cfb935e3 100644 --- a/server/middlewares/validators/videos/video-live.ts +++ b/server/middlewares/validators/videos/video-live.ts | |||
@@ -1,22 +1,28 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param } from 'express-validator' | 2 | import { body } from 'express-validator' |
3 | import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '@server/helpers/middlewares/videos' | 3 | import { CONSTRAINTS_FIELDS } from '@server/initializers/constants' |
4 | import { isLocalLiveVideoAccepted } from '@server/lib/moderation' | ||
5 | import { Hooks } from '@server/lib/plugins/hooks' | ||
6 | import { VideoModel } from '@server/models/video/video' | ||
4 | import { VideoLiveModel } from '@server/models/video/video-live' | 7 | import { VideoLiveModel } from '@server/models/video/video-live' |
8 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
5 | import { ServerErrorCode, UserRight, VideoState } from '@shared/models' | 9 | import { ServerErrorCode, UserRight, VideoState } from '@shared/models' |
6 | import { isBooleanValid, isIdOrUUIDValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' | 10 | import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' |
7 | import { isVideoNameValid } from '../../../helpers/custom-validators/videos' | 11 | import { isVideoNameValid } from '../../../helpers/custom-validators/videos' |
8 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 12 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
9 | import { logger } from '../../../helpers/logger' | 13 | import { logger } from '../../../helpers/logger' |
10 | import { CONFIG } from '../../../initializers/config' | 14 | import { CONFIG } from '../../../initializers/config' |
11 | import { areValidationErrors } from '../utils' | 15 | import { |
16 | areValidationErrors, | ||
17 | checkUserCanManageVideo, | ||
18 | doesVideoChannelOfAccountExist, | ||
19 | doesVideoExist, | ||
20 | isValidVideoIdParam | ||
21 | } from '../shared' | ||
12 | import { getCommonVideoEditAttributes } from './videos' | 22 | import { getCommonVideoEditAttributes } from './videos' |
13 | import { VideoModel } from '@server/models/video/video' | ||
14 | import { Hooks } from '@server/lib/plugins/hooks' | ||
15 | import { isLocalLiveVideoAccepted } from '@server/lib/moderation' | ||
16 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
17 | 23 | ||
18 | const videoLiveGetValidator = [ | 24 | const videoLiveGetValidator = [ |
19 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 25 | isValidVideoIdParam('videoId'), |
20 | 26 | ||
21 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 27 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
22 | logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username }) | 28 | logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username }) |
@@ -29,7 +35,12 @@ const videoLiveGetValidator = [ | |||
29 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return | 35 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return |
30 | 36 | ||
31 | const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id) | 37 | const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id) |
32 | if (!videoLive) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 38 | if (!videoLive) { |
39 | return res.fail({ | ||
40 | status: HttpStatusCode.NOT_FOUND_404, | ||
41 | message: 'Live video not found' | ||
42 | }) | ||
43 | } | ||
33 | 44 | ||
34 | res.locals.videoLive = videoLive | 45 | res.locals.videoLive = videoLive |
35 | 46 | ||
@@ -43,7 +54,9 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([ | |||
43 | .custom(isIdValid).withMessage('Should have correct video channel id'), | 54 | .custom(isIdValid).withMessage('Should have correct video channel id'), |
44 | 55 | ||
45 | body('name') | 56 | body('name') |
46 | .custom(isVideoNameValid).withMessage('Should have a valid name'), | 57 | .custom(isVideoNameValid).withMessage( |
58 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` | ||
59 | ), | ||
47 | 60 | ||
48 | body('saveReplay') | 61 | body('saveReplay') |
49 | .optional() | 62 | .optional() |
@@ -63,22 +76,27 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([ | |||
63 | if (CONFIG.LIVE.ENABLED !== true) { | 76 | if (CONFIG.LIVE.ENABLED !== true) { |
64 | cleanUpReqFiles(req) | 77 | cleanUpReqFiles(req) |
65 | 78 | ||
66 | return res.status(HttpStatusCode.FORBIDDEN_403) | 79 | return res.fail({ |
67 | .json({ error: 'Live is not enabled on this instance' }) | 80 | status: HttpStatusCode.FORBIDDEN_403, |
81 | message: 'Live is not enabled on this instance', | ||
82 | type: ServerErrorCode.LIVE_NOT_ENABLED | ||
83 | }) | ||
68 | } | 84 | } |
69 | 85 | ||
70 | if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { | 86 | if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { |
71 | cleanUpReqFiles(req) | 87 | cleanUpReqFiles(req) |
72 | 88 | ||
73 | return res.status(HttpStatusCode.FORBIDDEN_403) | 89 | return res.fail({ |
74 | .json({ error: 'Saving live replay is not allowed instance' }) | 90 | status: HttpStatusCode.FORBIDDEN_403, |
91 | message: 'Saving live replay is not enabled on this instance', | ||
92 | type: ServerErrorCode.LIVE_NOT_ALLOWING_REPLAY | ||
93 | }) | ||
75 | } | 94 | } |
76 | 95 | ||
77 | if (req.body.permanentLive && req.body.saveReplay) { | 96 | if (req.body.permanentLive && req.body.saveReplay) { |
78 | cleanUpReqFiles(req) | 97 | cleanUpReqFiles(req) |
79 | 98 | ||
80 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 99 | return res.fail({ message: 'Cannot set this live as permanent while saving its replay' }) |
81 | .json({ error: 'Cannot set this live as permanent while saving its replay' }) | ||
82 | } | 100 | } |
83 | 101 | ||
84 | const user = res.locals.oauth.token.User | 102 | const user = res.locals.oauth.token.User |
@@ -90,11 +108,11 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([ | |||
90 | if (totalInstanceLives >= CONFIG.LIVE.MAX_INSTANCE_LIVES) { | 108 | if (totalInstanceLives >= CONFIG.LIVE.MAX_INSTANCE_LIVES) { |
91 | cleanUpReqFiles(req) | 109 | cleanUpReqFiles(req) |
92 | 110 | ||
93 | return res.status(HttpStatusCode.FORBIDDEN_403) | 111 | return res.fail({ |
94 | .json({ | 112 | status: HttpStatusCode.FORBIDDEN_403, |
95 | code: ServerErrorCode.MAX_INSTANCE_LIVES_LIMIT_REACHED, | 113 | message: 'Cannot create this live because the max instance lives limit is reached.', |
96 | error: 'Cannot create this live because the max instance lives limit is reached.' | 114 | type: ServerErrorCode.MAX_INSTANCE_LIVES_LIMIT_REACHED |
97 | }) | 115 | }) |
98 | } | 116 | } |
99 | } | 117 | } |
100 | 118 | ||
@@ -104,11 +122,11 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([ | |||
104 | if (totalUserLives >= CONFIG.LIVE.MAX_USER_LIVES) { | 122 | if (totalUserLives >= CONFIG.LIVE.MAX_USER_LIVES) { |
105 | cleanUpReqFiles(req) | 123 | cleanUpReqFiles(req) |
106 | 124 | ||
107 | return res.status(HttpStatusCode.FORBIDDEN_403) | 125 | return res.fail({ |
108 | .json({ | 126 | status: HttpStatusCode.FORBIDDEN_403, |
109 | code: ServerErrorCode.MAX_USER_LIVES_LIMIT_REACHED, | 127 | message: 'Cannot create this live because the max user lives limit is reached.', |
110 | error: 'Cannot create this live because the max user lives limit is reached.' | 128 | type: ServerErrorCode.MAX_USER_LIVES_LIMIT_REACHED |
111 | }) | 129 | }) |
112 | } | 130 | } |
113 | } | 131 | } |
114 | 132 | ||
@@ -130,18 +148,18 @@ const videoLiveUpdateValidator = [ | |||
130 | if (areValidationErrors(req, res)) return | 148 | if (areValidationErrors(req, res)) return |
131 | 149 | ||
132 | if (req.body.permanentLive && req.body.saveReplay) { | 150 | if (req.body.permanentLive && req.body.saveReplay) { |
133 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 151 | return res.fail({ message: 'Cannot set this live as permanent while saving its replay' }) |
134 | .json({ error: 'Cannot set this live as permanent while saving its replay' }) | ||
135 | } | 152 | } |
136 | 153 | ||
137 | if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { | 154 | if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { |
138 | return res.status(HttpStatusCode.FORBIDDEN_403) | 155 | return res.fail({ |
139 | .json({ error: 'Saving live replay is not allowed instance' }) | 156 | status: HttpStatusCode.FORBIDDEN_403, |
157 | message: 'Saving live replay is not allowed instance' | ||
158 | }) | ||
140 | } | 159 | } |
141 | 160 | ||
142 | if (res.locals.videoAll.state !== VideoState.WAITING_FOR_LIVE) { | 161 | if (res.locals.videoAll.state !== VideoState.WAITING_FOR_LIVE) { |
143 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 162 | return res.fail({ message: 'Cannot update a live that has already started' }) |
144 | .json({ error: 'Cannot update a live that has already started' }) | ||
145 | } | 163 | } |
146 | 164 | ||
147 | // Check the user can manage the live | 165 | // Check the user can manage the live |
@@ -177,9 +195,10 @@ async function isLiveVideoAccepted (req: express.Request, res: express.Response) | |||
177 | if (!acceptedResult || acceptedResult.accepted !== true) { | 195 | if (!acceptedResult || acceptedResult.accepted !== true) { |
178 | logger.info('Refused local live video.', { acceptedResult, acceptParameters }) | 196 | logger.info('Refused local live video.', { acceptedResult, acceptParameters }) |
179 | 197 | ||
180 | res.status(HttpStatusCode.FORBIDDEN_403) | 198 | res.fail({ |
181 | .json({ error: acceptedResult.errorMessage || 'Refused local live video' }) | 199 | status: HttpStatusCode.FORBIDDEN_403, |
182 | 200 | message: acceptedResult.errorMessage || 'Refused local live video' | |
201 | }) | ||
183 | return false | 202 | return false |
184 | } | 203 | } |
185 | 204 | ||
diff --git a/server/middlewares/validators/videos/video-ownership-changes.ts b/server/middlewares/validators/videos/video-ownership-changes.ts new file mode 100644 index 000000000..54ac46c99 --- /dev/null +++ b/server/middlewares/validators/videos/video-ownership-changes.ts | |||
@@ -0,0 +1,121 @@ | |||
1 | import * as express from 'express' | ||
2 | import { param } from 'express-validator' | ||
3 | import { isIdValid } from '@server/helpers/custom-validators/misc' | ||
4 | import { checkUserCanTerminateOwnershipChange } from '@server/helpers/custom-validators/video-ownership' | ||
5 | import { logger } from '@server/helpers/logger' | ||
6 | import { isAbleToUploadVideo } from '@server/lib/user' | ||
7 | import { AccountModel } from '@server/models/account/account' | ||
8 | import { MVideoWithAllFiles } from '@server/types/models' | ||
9 | import { HttpStatusCode } from '@shared/core-utils' | ||
10 | import { ServerErrorCode, UserRight, VideoChangeOwnershipAccept, VideoChangeOwnershipStatus, VideoState } from '@shared/models' | ||
11 | import { | ||
12 | areValidationErrors, | ||
13 | checkUserCanManageVideo, | ||
14 | doesChangeVideoOwnershipExist, | ||
15 | doesVideoChannelOfAccountExist, | ||
16 | doesVideoExist, | ||
17 | isValidVideoIdParam | ||
18 | } from '../shared' | ||
19 | |||
20 | const videosChangeOwnershipValidator = [ | ||
21 | isValidVideoIdParam('videoId'), | ||
22 | |||
23 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
24 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
25 | |||
26 | if (areValidationErrors(req, res)) return | ||
27 | if (!await doesVideoExist(req.params.videoId, res)) return | ||
28 | |||
29 | // Check if the user who did the request is able to change the ownership of the video | ||
30 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return | ||
31 | |||
32 | const nextOwner = await AccountModel.loadLocalByName(req.body.username) | ||
33 | if (!nextOwner) { | ||
34 | res.fail({ message: 'Changing video ownership to a remote account is not supported yet' }) | ||
35 | return | ||
36 | } | ||
37 | |||
38 | res.locals.nextOwner = nextOwner | ||
39 | return next() | ||
40 | } | ||
41 | ] | ||
42 | |||
43 | const videosTerminateChangeOwnershipValidator = [ | ||
44 | param('id') | ||
45 | .custom(isIdValid).withMessage('Should have a valid id'), | ||
46 | |||
47 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
48 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
49 | |||
50 | if (areValidationErrors(req, res)) return | ||
51 | if (!await doesChangeVideoOwnershipExist(req.params.id, res)) return | ||
52 | |||
53 | // Check if the user who did the request is able to change the ownership of the video | ||
54 | if (!checkUserCanTerminateOwnershipChange(res.locals.oauth.token.User, res.locals.videoChangeOwnership, res)) return | ||
55 | |||
56 | const videoChangeOwnership = res.locals.videoChangeOwnership | ||
57 | |||
58 | if (videoChangeOwnership.status !== VideoChangeOwnershipStatus.WAITING) { | ||
59 | res.fail({ | ||
60 | status: HttpStatusCode.FORBIDDEN_403, | ||
61 | message: 'Ownership already accepted or refused' | ||
62 | }) | ||
63 | return | ||
64 | } | ||
65 | |||
66 | return next() | ||
67 | } | ||
68 | ] | ||
69 | |||
70 | const videosAcceptChangeOwnershipValidator = [ | ||
71 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
72 | const body = req.body as VideoChangeOwnershipAccept | ||
73 | if (!await doesVideoChannelOfAccountExist(body.channelId, res.locals.oauth.token.User, res)) return | ||
74 | |||
75 | const videoChangeOwnership = res.locals.videoChangeOwnership | ||
76 | |||
77 | const video = videoChangeOwnership.Video | ||
78 | |||
79 | if (!await checkCanAccept(video, res)) return | ||
80 | |||
81 | return next() | ||
82 | } | ||
83 | ] | ||
84 | |||
85 | export { | ||
86 | videosChangeOwnershipValidator, | ||
87 | videosTerminateChangeOwnershipValidator, | ||
88 | videosAcceptChangeOwnershipValidator | ||
89 | } | ||
90 | |||
91 | // --------------------------------------------------------------------------- | ||
92 | |||
93 | async function checkCanAccept (video: MVideoWithAllFiles, res: express.Response): Promise<boolean> { | ||
94 | if (video.isLive) { | ||
95 | |||
96 | if (video.state !== VideoState.WAITING_FOR_LIVE) { | ||
97 | res.fail({ | ||
98 | status: HttpStatusCode.BAD_REQUEST_400, | ||
99 | message: 'You can accept an ownership change of a published live.' | ||
100 | }) | ||
101 | |||
102 | return false | ||
103 | } | ||
104 | |||
105 | return true | ||
106 | } | ||
107 | |||
108 | const user = res.locals.oauth.token.User | ||
109 | |||
110 | if (!await isAbleToUploadVideo(user.id, video.getMaxQualityFile().size)) { | ||
111 | res.fail({ | ||
112 | status: HttpStatusCode.PAYLOAD_TOO_LARGE_413, | ||
113 | message: 'The user video quota is exceeded with this video.', | ||
114 | type: ServerErrorCode.QUOTA_REACHED | ||
115 | }) | ||
116 | |||
117 | return false | ||
118 | } | ||
119 | |||
120 | return true | ||
121 | } | ||
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts index c872d045e..5ee7ee0ce 100644 --- a/server/middlewares/validators/videos/video-playlists.ts +++ b/server/middlewares/validators/videos/video-playlists.ts | |||
@@ -11,6 +11,7 @@ import { | |||
11 | isIdOrUUIDValid, | 11 | isIdOrUUIDValid, |
12 | isIdValid, | 12 | isIdValid, |
13 | isUUIDValid, | 13 | isUUIDValid, |
14 | toCompleteUUID, | ||
14 | toIntArray, | 15 | toIntArray, |
15 | toIntOrNull, | 16 | toIntOrNull, |
16 | toValueOrNull | 17 | toValueOrNull |
@@ -25,12 +26,18 @@ import { | |||
25 | import { isVideoImage } from '../../../helpers/custom-validators/videos' | 26 | import { isVideoImage } from '../../../helpers/custom-validators/videos' |
26 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 27 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
27 | import { logger } from '../../../helpers/logger' | 28 | import { logger } from '../../../helpers/logger' |
28 | import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares' | ||
29 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' | 29 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' |
30 | import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' | 30 | import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' |
31 | import { MVideoPlaylist } from '../../../types/models/video/video-playlist' | 31 | import { MVideoPlaylist } from '../../../types/models/video/video-playlist' |
32 | import { authenticatePromiseIfNeeded } from '../../auth' | 32 | import { authenticatePromiseIfNeeded } from '../../auth' |
33 | import { areValidationErrors } from '../utils' | 33 | import { |
34 | areValidationErrors, | ||
35 | doesVideoChannelIdExist, | ||
36 | doesVideoExist, | ||
37 | doesVideoPlaylistExist, | ||
38 | isValidPlaylistIdParam, | ||
39 | VideoPlaylistFetchType | ||
40 | } from '../shared' | ||
34 | 41 | ||
35 | const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ | 42 | const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ |
36 | body('displayName') | 43 | body('displayName') |
@@ -44,10 +51,13 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ | |||
44 | const body: VideoPlaylistCreate = req.body | 51 | const body: VideoPlaylistCreate = req.body |
45 | if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) | 52 | if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) |
46 | 53 | ||
47 | if (body.privacy === VideoPlaylistPrivacy.PUBLIC && !body.videoChannelId) { | 54 | if ( |
55 | !body.videoChannelId && | ||
56 | (body.privacy === VideoPlaylistPrivacy.PUBLIC || body.privacy === VideoPlaylistPrivacy.UNLISTED) | ||
57 | ) { | ||
48 | cleanUpReqFiles(req) | 58 | cleanUpReqFiles(req) |
49 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 59 | |
50 | .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' }) | 60 | return res.fail({ message: 'Cannot set "public" or "unlisted" a playlist that is not assigned to a channel.' }) |
51 | } | 61 | } |
52 | 62 | ||
53 | return next() | 63 | return next() |
@@ -55,8 +65,7 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ | |||
55 | ]) | 65 | ]) |
56 | 66 | ||
57 | const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ | 67 | const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ |
58 | param('playlistId') | 68 | isValidPlaylistIdParam('playlistId'), |
59 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
60 | 69 | ||
61 | body('displayName') | 70 | body('displayName') |
62 | .optional() | 71 | .optional() |
@@ -85,14 +94,14 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ | |||
85 | ) | 94 | ) |
86 | ) { | 95 | ) { |
87 | cleanUpReqFiles(req) | 96 | cleanUpReqFiles(req) |
88 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 97 | |
89 | .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' }) | 98 | return res.fail({ message: 'Cannot set "public" a playlist that is not assigned to a channel.' }) |
90 | } | 99 | } |
91 | 100 | ||
92 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { | 101 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { |
93 | cleanUpReqFiles(req) | 102 | cleanUpReqFiles(req) |
94 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 103 | |
95 | .json({ error: 'Cannot update a watch later playlist.' }) | 104 | return res.fail({ message: 'Cannot update a watch later playlist.' }) |
96 | } | 105 | } |
97 | 106 | ||
98 | if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) | 107 | if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) |
@@ -102,8 +111,7 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ | |||
102 | ]) | 111 | ]) |
103 | 112 | ||
104 | const videoPlaylistsDeleteValidator = [ | 113 | const videoPlaylistsDeleteValidator = [ |
105 | param('playlistId') | 114 | isValidPlaylistIdParam('playlistId'), |
106 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
107 | 115 | ||
108 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 116 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
109 | logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params }) | 117 | logger.debug('Checking videoPlaylistsDeleteValidator parameters', { parameters: req.params }) |
@@ -114,8 +122,7 @@ const videoPlaylistsDeleteValidator = [ | |||
114 | 122 | ||
115 | const videoPlaylist = getPlaylist(res) | 123 | const videoPlaylist = getPlaylist(res) |
116 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { | 124 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { |
117 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 125 | return res.fail({ message: 'Cannot delete a watch later playlist.' }) |
118 | .json({ error: 'Cannot delete a watch later playlist.' }) | ||
119 | } | 126 | } |
120 | 127 | ||
121 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { | 128 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { |
@@ -128,8 +135,7 @@ const videoPlaylistsDeleteValidator = [ | |||
128 | 135 | ||
129 | const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { | 136 | const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { |
130 | return [ | 137 | return [ |
131 | param('playlistId') | 138 | isValidPlaylistIdParam('playlistId'), |
132 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
133 | 139 | ||
134 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 140 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
135 | logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) | 141 | logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) |
@@ -144,7 +150,10 @@ const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { | |||
144 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { | 150 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { |
145 | if (isUUIDValid(req.params.playlistId)) return next() | 151 | if (isUUIDValid(req.params.playlistId)) return next() |
146 | 152 | ||
147 | return res.status(HttpStatusCode.NOT_FOUND_404).end() | 153 | return res.fail({ |
154 | status: HttpStatusCode.NOT_FOUND_404, | ||
155 | message: 'Playlist not found' | ||
156 | }) | ||
148 | } | 157 | } |
149 | 158 | ||
150 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { | 159 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { |
@@ -156,8 +165,10 @@ const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { | |||
156 | !user || | 165 | !user || |
157 | (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) | 166 | (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) |
158 | ) { | 167 | ) { |
159 | return res.status(HttpStatusCode.FORBIDDEN_403) | 168 | return res.fail({ |
160 | .json({ error: 'Cannot get this private video playlist.' }) | 169 | status: HttpStatusCode.FORBIDDEN_403, |
170 | message: 'Cannot get this private video playlist.' | ||
171 | }) | ||
161 | } | 172 | } |
162 | 173 | ||
163 | return next() | 174 | return next() |
@@ -181,9 +192,10 @@ const videoPlaylistsSearchValidator = [ | |||
181 | ] | 192 | ] |
182 | 193 | ||
183 | const videoPlaylistsAddVideoValidator = [ | 194 | const videoPlaylistsAddVideoValidator = [ |
184 | param('playlistId') | 195 | isValidPlaylistIdParam('playlistId'), |
185 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | 196 | |
186 | body('videoId') | 197 | body('videoId') |
198 | .customSanitizer(toCompleteUUID) | ||
187 | .custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'), | 199 | .custom(isIdOrUUIDValid).withMessage('Should have a valid video id/uuid'), |
188 | body('startTimestamp') | 200 | body('startTimestamp') |
189 | .optional() | 201 | .optional() |
@@ -211,9 +223,9 @@ const videoPlaylistsAddVideoValidator = [ | |||
211 | ] | 223 | ] |
212 | 224 | ||
213 | const videoPlaylistsUpdateOrRemoveVideoValidator = [ | 225 | const videoPlaylistsUpdateOrRemoveVideoValidator = [ |
214 | param('playlistId') | 226 | isValidPlaylistIdParam('playlistId'), |
215 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
216 | param('playlistElementId') | 227 | param('playlistElementId') |
228 | .customSanitizer(toCompleteUUID) | ||
217 | .custom(isIdValid).withMessage('Should have an element id/uuid'), | 229 | .custom(isIdValid).withMessage('Should have an element id/uuid'), |
218 | body('startTimestamp') | 230 | body('startTimestamp') |
219 | .optional() | 231 | .optional() |
@@ -233,10 +245,10 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [ | |||
233 | 245 | ||
234 | const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) | 246 | const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) |
235 | if (!videoPlaylistElement) { | 247 | if (!videoPlaylistElement) { |
236 | res.status(HttpStatusCode.NOT_FOUND_404) | 248 | res.fail({ |
237 | .json({ error: 'Video playlist element not found' }) | 249 | status: HttpStatusCode.NOT_FOUND_404, |
238 | .end() | 250 | message: 'Video playlist element not found' |
239 | 251 | }) | |
240 | return | 252 | return |
241 | } | 253 | } |
242 | res.locals.videoPlaylistElement = videoPlaylistElement | 254 | res.locals.videoPlaylistElement = videoPlaylistElement |
@@ -248,8 +260,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [ | |||
248 | ] | 260 | ] |
249 | 261 | ||
250 | const videoPlaylistElementAPGetValidator = [ | 262 | const videoPlaylistElementAPGetValidator = [ |
251 | param('playlistId') | 263 | isValidPlaylistIdParam('playlistId'), |
252 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
253 | param('playlistElementId') | 264 | param('playlistElementId') |
254 | .custom(isIdValid).withMessage('Should have an playlist element id'), | 265 | .custom(isIdValid).withMessage('Should have an playlist element id'), |
255 | 266 | ||
@@ -263,15 +274,18 @@ const videoPlaylistElementAPGetValidator = [ | |||
263 | 274 | ||
264 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId) | 275 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId) |
265 | if (!videoPlaylistElement) { | 276 | if (!videoPlaylistElement) { |
266 | res.status(HttpStatusCode.NOT_FOUND_404) | 277 | res.fail({ |
267 | .json({ error: 'Video playlist element not found' }) | 278 | status: HttpStatusCode.NOT_FOUND_404, |
268 | .end() | 279 | message: 'Video playlist element not found' |
269 | 280 | }) | |
270 | return | 281 | return |
271 | } | 282 | } |
272 | 283 | ||
273 | if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { | 284 | if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { |
274 | return res.status(HttpStatusCode.FORBIDDEN_403).end() | 285 | return res.fail({ |
286 | status: HttpStatusCode.FORBIDDEN_403, | ||
287 | message: 'Cannot get this private video playlist.' | ||
288 | }) | ||
275 | } | 289 | } |
276 | 290 | ||
277 | res.locals.videoPlaylistElementAP = videoPlaylistElement | 291 | res.locals.videoPlaylistElementAP = videoPlaylistElement |
@@ -281,8 +295,7 @@ const videoPlaylistElementAPGetValidator = [ | |||
281 | ] | 295 | ] |
282 | 296 | ||
283 | const videoPlaylistsReorderVideosValidator = [ | 297 | const videoPlaylistsReorderVideosValidator = [ |
284 | param('playlistId') | 298 | isValidPlaylistIdParam('playlistId'), |
285 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
286 | body('startPosition') | 299 | body('startPosition') |
287 | .isInt({ min: 1 }).withMessage('Should have a valid start position'), | 300 | .isInt({ min: 1 }).withMessage('Should have a valid start position'), |
288 | body('insertAfterPosition') | 301 | body('insertAfterPosition') |
@@ -307,18 +320,12 @@ const videoPlaylistsReorderVideosValidator = [ | |||
307 | const reorderLength: number = req.body.reorderLength | 320 | const reorderLength: number = req.body.reorderLength |
308 | 321 | ||
309 | if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) { | 322 | if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) { |
310 | res.status(HttpStatusCode.BAD_REQUEST_400) | 323 | res.fail({ message: `Start position or insert after position exceed the playlist limits (max: ${nextPosition - 1})` }) |
311 | .json({ error: `Start position or insert after position exceed the playlist limits (max: ${nextPosition - 1})` }) | ||
312 | .end() | ||
313 | |||
314 | return | 324 | return |
315 | } | 325 | } |
316 | 326 | ||
317 | if (reorderLength && reorderLength + startPosition > nextPosition) { | 327 | if (reorderLength && reorderLength + startPosition > nextPosition) { |
318 | res.status(HttpStatusCode.BAD_REQUEST_400) | 328 | res.fail({ message: `Reorder length with this start position exceeds the playlist limits (max: ${nextPosition - startPosition})` }) |
319 | .json({ error: `Reorder length with this start position exceeds the playlist limits (max: ${nextPosition - startPosition})` }) | ||
320 | .end() | ||
321 | |||
322 | return | 329 | return |
323 | } | 330 | } |
324 | 331 | ||
@@ -401,10 +408,10 @@ function getCommonPlaylistEditAttributes () { | |||
401 | 408 | ||
402 | function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { | 409 | function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { |
403 | if (videoPlaylist.isOwned() === false) { | 410 | if (videoPlaylist.isOwned() === false) { |
404 | res.status(HttpStatusCode.FORBIDDEN_403) | 411 | res.fail({ |
405 | .json({ error: 'Cannot manage video playlist of another server.' }) | 412 | status: HttpStatusCode.FORBIDDEN_403, |
406 | .end() | 413 | message: 'Cannot manage video playlist of another server.' |
407 | 414 | }) | |
408 | return false | 415 | return false |
409 | } | 416 | } |
410 | 417 | ||
@@ -412,10 +419,10 @@ function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: M | |||
412 | // The user can delete it if s/he is an admin | 419 | // The user can delete it if s/he is an admin |
413 | // Or if s/he is the video playlist's owner | 420 | // Or if s/he is the video playlist's owner |
414 | if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) { | 421 | if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) { |
415 | res.status(HttpStatusCode.FORBIDDEN_403) | 422 | res.fail({ |
416 | .json({ error: 'Cannot manage video playlist of another user' }) | 423 | status: HttpStatusCode.FORBIDDEN_403, |
417 | .end() | 424 | message: 'Cannot manage video playlist of another user' |
418 | 425 | }) | |
419 | return false | 426 | return false |
420 | } | 427 | } |
421 | 428 | ||
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts index 01bdef25f..5d5dfb222 100644 --- a/server/middlewares/validators/videos/video-rates.ts +++ b/server/middlewares/validators/videos/video-rates.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' | 3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
4 | import { VideoRateType } from '../../../../shared/models/videos' | ||
5 | import { isAccountNameValid } from '../../../helpers/custom-validators/accounts' | ||
6 | import { isIdValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { isRatingValid } from '../../../helpers/custom-validators/video-rates' | 7 | import { isRatingValid } from '../../../helpers/custom-validators/video-rates' |
5 | import { isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos' | 8 | import { isVideoRatingTypeValid } from '../../../helpers/custom-validators/videos' |
6 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
7 | import { areValidationErrors } from '../utils' | ||
8 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 10 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
9 | import { VideoRateType } from '../../../../shared/models/videos' | 11 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' |
10 | import { isAccountNameValid } from '../../../helpers/custom-validators/accounts' | ||
11 | import { doesVideoExist } from '../../../helpers/middlewares' | ||
12 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 12 | ||
14 | const videoUpdateRateValidator = [ | 13 | const videoUpdateRateValidator = [ |
15 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 14 | isValidVideoIdParam('id'), |
15 | |||
16 | body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), | 16 | body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'), |
17 | 17 | ||
18 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 18 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
@@ -37,8 +37,10 @@ const getAccountVideoRateValidatorFactory = function (rateType: VideoRateType) { | |||
37 | 37 | ||
38 | const rate = await AccountVideoRateModel.loadLocalAndPopulateVideo(rateType, req.params.name, +req.params.videoId) | 38 | const rate = await AccountVideoRateModel.loadLocalAndPopulateVideo(rateType, req.params.name, +req.params.videoId) |
39 | if (!rate) { | 39 | if (!rate) { |
40 | return res.status(HttpStatusCode.NOT_FOUND_404) | 40 | return res.fail({ |
41 | .json({ error: 'Video rate not found' }) | 41 | status: HttpStatusCode.NOT_FOUND_404, |
42 | message: 'Video rate not found' | ||
43 | }) | ||
42 | } | 44 | } |
43 | 45 | ||
44 | res.locals.accountVideoRate = rate | 46 | res.locals.accountVideoRate = rate |
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts index f0d8e0c36..7e54b6fc0 100644 --- a/server/middlewares/validators/videos/video-shares.ts +++ b/server/middlewares/validators/videos/video-shares.ts | |||
@@ -1,15 +1,16 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { param } from 'express-validator' | 2 | import { param } from 'express-validator' |
3 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' | 3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
4 | import { isIdValid } from '../../../helpers/custom-validators/misc' | ||
4 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
5 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
6 | import { areValidationErrors } from '../utils' | 7 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' |
7 | import { doesVideoExist } from '../../../helpers/middlewares' | ||
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
9 | 8 | ||
10 | const videosShareValidator = [ | 9 | const videosShareValidator = [ |
11 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 10 | isValidVideoIdParam('id'), |
12 | param('actorId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid actor id'), | 11 | |
12 | param('actorId') | ||
13 | .custom(isIdValid).not().isEmpty().withMessage('Should have a valid actor id'), | ||
13 | 14 | ||
14 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 15 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
15 | logger.debug('Checking videoShare parameters', { parameters: req.params }) | 16 | logger.debug('Checking videoShare parameters', { parameters: req.params }) |
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts index 29ce0dab6..43306f7cd 100644 --- a/server/middlewares/validators/videos/video-watch.ts +++ b/server/middlewares/validators/videos/video-watch.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import { body, param } from 'express-validator' | ||
2 | import * as express from 'express' | 1 | import * as express from 'express' |
3 | import { isIdOrUUIDValid, toIntOrNull } from '../../../helpers/custom-validators/misc' | 2 | import { body } from 'express-validator' |
4 | import { areValidationErrors } from '../utils' | ||
5 | import { logger } from '../../../helpers/logger' | ||
6 | import { doesVideoExist } from '../../../helpers/middlewares' | ||
7 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
4 | import { toIntOrNull } from '../../../helpers/custom-validators/misc' | ||
5 | import { logger } from '../../../helpers/logger' | ||
6 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' | ||
8 | 7 | ||
9 | const videoWatchingValidator = [ | 8 | const videoWatchingValidator = [ |
10 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 9 | isValidVideoIdParam('videoId'), |
10 | |||
11 | body('currentTime') | 11 | body('currentTime') |
12 | .customSanitizer(toIntOrNull) | 12 | .customSanitizer(toIntOrNull) |
13 | .isInt().withMessage('Should have correct current time'), | 13 | .isInt().withMessage('Should have correct current time'), |
@@ -21,7 +21,10 @@ const videoWatchingValidator = [ | |||
21 | const user = res.locals.oauth.token.User | 21 | const user = res.locals.oauth.token.User |
22 | if (user.videosHistoryEnabled === false) { | 22 | if (user.videosHistoryEnabled === false) { |
23 | logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id) | 23 | logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id) |
24 | return res.status(HttpStatusCode.CONFLICT_409).end() | 24 | return res.fail({ |
25 | status: HttpStatusCode.CONFLICT_409, | ||
26 | message: 'Video history is disabled' | ||
27 | }) | ||
25 | } | 28 | } |
26 | 29 | ||
27 | return next() | 30 | return next() |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index d26bcd4a6..49e10e2b5 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -4,16 +4,14 @@ import { getResumableUploadPath } from '@server/helpers/upload' | |||
4 | import { isAbleToUploadVideo } from '@server/lib/user' | 4 | import { isAbleToUploadVideo } from '@server/lib/user' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { ExpressPromiseHandler } from '@server/types/express' | 6 | import { ExpressPromiseHandler } from '@server/types/express' |
7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' | 7 | import { MUserAccountId, MVideoFullLight } from '@server/types/models' |
8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' | 8 | import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared' |
9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | ||
11 | import { | 10 | import { |
12 | exists, | 11 | exists, |
13 | isBooleanValid, | 12 | isBooleanValid, |
14 | isDateValid, | 13 | isDateValid, |
15 | isFileFieldValid, | 14 | isFileFieldValid, |
16 | isIdOrUUIDValid, | ||
17 | isIdValid, | 15 | isIdValid, |
18 | isUUIDValid, | 16 | isUUIDValid, |
19 | toArray, | 17 | toArray, |
@@ -22,7 +20,6 @@ import { | |||
22 | toValueOrNull | 20 | toValueOrNull |
23 | } from '../../../helpers/custom-validators/misc' | 21 | } from '../../../helpers/custom-validators/misc' |
24 | import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' | 22 | import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' |
25 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' | ||
26 | import { | 23 | import { |
27 | isScheduleVideoUpdatePrivacyValid, | 24 | isScheduleVideoUpdatePrivacyValid, |
28 | isVideoCategoryValid, | 25 | isVideoCategoryValid, |
@@ -42,22 +39,22 @@ import { | |||
42 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 39 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
43 | import { getDurationFromVideoFile } from '../../../helpers/ffprobe-utils' | 40 | import { getDurationFromVideoFile } from '../../../helpers/ffprobe-utils' |
44 | import { logger } from '../../../helpers/logger' | 41 | import { logger } from '../../../helpers/logger' |
45 | import { | ||
46 | checkUserCanManageVideo, | ||
47 | doesVideoChannelOfAccountExist, | ||
48 | doesVideoExist, | ||
49 | doesVideoFileOfVideoExist | ||
50 | } from '../../../helpers/middlewares' | ||
51 | import { deleteFileAndCatch } from '../../../helpers/utils' | 42 | import { deleteFileAndCatch } from '../../../helpers/utils' |
52 | import { getVideoWithAttributes } from '../../../helpers/video' | 43 | import { getVideoWithAttributes } from '../../../helpers/video' |
53 | import { CONFIG } from '../../../initializers/config' | 44 | import { CONFIG } from '../../../initializers/config' |
54 | import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants' | 45 | import { CONSTRAINTS_FIELDS, OVERVIEWS } from '../../../initializers/constants' |
55 | import { isLocalVideoAccepted } from '../../../lib/moderation' | 46 | import { isLocalVideoAccepted } from '../../../lib/moderation' |
56 | import { Hooks } from '../../../lib/plugins/hooks' | 47 | import { Hooks } from '../../../lib/plugins/hooks' |
57 | import { AccountModel } from '../../../models/account/account' | ||
58 | import { VideoModel } from '../../../models/video/video' | 48 | import { VideoModel } from '../../../models/video/video' |
59 | import { authenticatePromiseIfNeeded } from '../../auth' | 49 | import { authenticatePromiseIfNeeded } from '../../auth' |
60 | import { areValidationErrors } from '../utils' | 50 | import { |
51 | areValidationErrors, | ||
52 | checkUserCanManageVideo, | ||
53 | doesVideoChannelOfAccountExist, | ||
54 | doesVideoExist, | ||
55 | doesVideoFileOfVideoExist, | ||
56 | isValidVideoIdParam | ||
57 | } from '../shared' | ||
61 | 58 | ||
62 | const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ | 59 | const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ |
63 | body('videofile') | 60 | body('videofile') |
@@ -65,8 +62,9 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ | |||
65 | .withMessage('Should have a file'), | 62 | .withMessage('Should have a file'), |
66 | body('name') | 63 | body('name') |
67 | .trim() | 64 | .trim() |
68 | .custom(isVideoNameValid) | 65 | .custom(isVideoNameValid).withMessage( |
69 | .withMessage('Should have a valid name'), | 66 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` |
67 | ), | ||
70 | body('channelId') | 68 | body('channelId') |
71 | .customSanitizer(toIntOrNull) | 69 | .customSanitizer(toIntOrNull) |
72 | .custom(isIdValid).withMessage('Should have correct video channel id'), | 70 | .custom(isIdValid).withMessage('Should have correct video channel id'), |
@@ -87,9 +85,11 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([ | |||
87 | if (!videoFile.duration) await addDurationToVideo(videoFile) | 85 | if (!videoFile.duration) await addDurationToVideo(videoFile) |
88 | } catch (err) { | 86 | } catch (err) { |
89 | logger.error('Invalid input file in videosAddLegacyValidator.', { err }) | 87 | logger.error('Invalid input file in videosAddLegacyValidator.', { err }) |
90 | res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422) | ||
91 | .json({ error: 'Video file unreadable.' }) | ||
92 | 88 | ||
89 | res.fail({ | ||
90 | status: HttpStatusCode.UNPROCESSABLE_ENTITY_422, | ||
91 | message: 'Video file unreadable.' | ||
92 | }) | ||
93 | return cleanUpReqFiles(req) | 93 | return cleanUpReqFiles(req) |
94 | } | 94 | } |
95 | 95 | ||
@@ -117,9 +117,11 @@ const videosAddResumableValidator = [ | |||
117 | if (!file.duration) await addDurationToVideo(file) | 117 | if (!file.duration) await addDurationToVideo(file) |
118 | } catch (err) { | 118 | } catch (err) { |
119 | logger.error('Invalid input file in videosAddResumableValidator.', { err }) | 119 | logger.error('Invalid input file in videosAddResumableValidator.', { err }) |
120 | res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422) | ||
121 | .json({ error: 'Video file unreadable.' }) | ||
122 | 120 | ||
121 | res.fail({ | ||
122 | status: HttpStatusCode.UNPROCESSABLE_ENTITY_422, | ||
123 | message: 'Video file unreadable.' | ||
124 | }) | ||
123 | return cleanup() | 125 | return cleanup() |
124 | } | 126 | } |
125 | 127 | ||
@@ -146,8 +148,9 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([ | |||
146 | .withMessage('Should have a valid filename'), | 148 | .withMessage('Should have a valid filename'), |
147 | body('name') | 149 | body('name') |
148 | .trim() | 150 | .trim() |
149 | .custom(isVideoNameValid) | 151 | .custom(isVideoNameValid).withMessage( |
150 | .withMessage('Should have a valid name'), | 152 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` |
153 | ), | ||
151 | body('channelId') | 154 | body('channelId') |
152 | .customSanitizer(toIntOrNull) | 155 | .customSanitizer(toIntOrNull) |
153 | .custom(isIdValid).withMessage('Should have correct video channel id'), | 156 | .custom(isIdValid).withMessage('Should have correct video channel id'), |
@@ -192,11 +195,14 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([ | |||
192 | ]) | 195 | ]) |
193 | 196 | ||
194 | const videosUpdateValidator = getCommonVideoEditAttributes().concat([ | 197 | const videosUpdateValidator = getCommonVideoEditAttributes().concat([ |
195 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 198 | isValidVideoIdParam('id'), |
199 | |||
196 | body('name') | 200 | body('name') |
197 | .optional() | 201 | .optional() |
198 | .trim() | 202 | .trim() |
199 | .custom(isVideoNameValid).withMessage('Should have a valid name'), | 203 | .custom(isVideoNameValid).withMessage( |
204 | `Should have a video name between ${CONSTRAINTS_FIELDS.VIDEOS.NAME.min} and ${CONSTRAINTS_FIELDS.VIDEOS.NAME.max} characters long` | ||
205 | ), | ||
200 | body('channelId') | 206 | body('channelId') |
201 | .optional() | 207 | .optional() |
202 | .customSanitizer(toIntOrNull) | 208 | .customSanitizer(toIntOrNull) |
@@ -238,20 +244,22 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R | |||
238 | const serverActor = await getServerActor() | 244 | const serverActor = await getServerActor() |
239 | if (await VideoModel.checkVideoHasInstanceFollow(video.id, serverActor.id) === true) return next() | 245 | if (await VideoModel.checkVideoHasInstanceFollow(video.id, serverActor.id) === true) return next() |
240 | 246 | ||
241 | return res.status(HttpStatusCode.FORBIDDEN_403) | 247 | return res.fail({ |
242 | .json({ | 248 | status: HttpStatusCode.FORBIDDEN_403, |
243 | errorCode: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS, | 249 | message: 'Cannot get this video regarding follow constraints', |
244 | error: 'Cannot get this video regarding follow constraints.', | 250 | type: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS, |
245 | originUrl: video.url | 251 | data: { |
246 | }) | 252 | originUrl: video.url |
253 | } | ||
254 | }) | ||
247 | } | 255 | } |
248 | 256 | ||
249 | const videosCustomGetValidator = ( | 257 | const videosCustomGetValidator = ( |
250 | fetchType: 'all' | 'only-video' | 'only-video-with-rights' | 'only-immutable-attributes', | 258 | fetchType: 'for-api' | 'all' | 'only-video' | 'only-immutable-attributes', |
251 | authenticateInQuery = false | 259 | authenticateInQuery = false |
252 | ) => { | 260 | ) => { |
253 | return [ | 261 | return [ |
254 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 262 | isValidVideoIdParam('id'), |
255 | 263 | ||
256 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 264 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
257 | logger.debug('Checking videosGet parameters', { parameters: req.params }) | 265 | logger.debug('Checking videosGet parameters', { parameters: req.params }) |
@@ -262,7 +270,7 @@ const videosCustomGetValidator = ( | |||
262 | // Controllers does not need to check video rights | 270 | // Controllers does not need to check video rights |
263 | if (fetchType === 'only-immutable-attributes') return next() | 271 | if (fetchType === 'only-immutable-attributes') return next() |
264 | 272 | ||
265 | const video = getVideoWithAttributes(res) as MVideoWithRights | 273 | const video = getVideoWithAttributes(res) as MVideoFullLight |
266 | 274 | ||
267 | // Video private or blacklisted | 275 | // Video private or blacklisted |
268 | if (video.requiresAuth()) { | 276 | if (video.requiresAuth()) { |
@@ -270,10 +278,12 @@ const videosCustomGetValidator = ( | |||
270 | 278 | ||
271 | const user = res.locals.oauth ? res.locals.oauth.token.User : null | 279 | const user = res.locals.oauth ? res.locals.oauth.token.User : null |
272 | 280 | ||
273 | // Only the owner or a user that have blacklist rights can see the video | 281 | // Only the owner or a user that have blocklist rights can see the video |
274 | if (!user || !user.canGetVideo(video)) { | 282 | if (!user || !user.canGetVideo(video)) { |
275 | return res.status(HttpStatusCode.FORBIDDEN_403) | 283 | return res.fail({ |
276 | .json({ error: 'Cannot get this private/internal or blacklisted video.' }) | 284 | status: HttpStatusCode.FORBIDDEN_403, |
285 | message: 'Cannot get this private/internal or blocklisted video' | ||
286 | }) | ||
277 | } | 287 | } |
278 | 288 | ||
279 | return next() | 289 | return next() |
@@ -287,7 +297,10 @@ const videosCustomGetValidator = ( | |||
287 | if (isUUIDValid(req.params.id)) return next() | 297 | if (isUUIDValid(req.params.id)) return next() |
288 | 298 | ||
289 | // Don't leak this unlisted video | 299 | // Don't leak this unlisted video |
290 | return res.status(HttpStatusCode.NOT_FOUND_404).end() | 300 | return res.fail({ |
301 | status: HttpStatusCode.NOT_FOUND_404, | ||
302 | message: 'Video not found' | ||
303 | }) | ||
291 | } | 304 | } |
292 | } | 305 | } |
293 | ] | 306 | ] |
@@ -297,8 +310,10 @@ const videosGetValidator = videosCustomGetValidator('all') | |||
297 | const videosDownloadValidator = videosCustomGetValidator('all', true) | 310 | const videosDownloadValidator = videosCustomGetValidator('all', true) |
298 | 311 | ||
299 | const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([ | 312 | const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([ |
300 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 313 | isValidVideoIdParam('id'), |
301 | param('videoFileId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid videoFileId'), | 314 | |
315 | param('videoFileId') | ||
316 | .custom(isIdValid).not().isEmpty().withMessage('Should have a valid videoFileId'), | ||
302 | 317 | ||
303 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 318 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
304 | logger.debug('Checking videoFileMetadataGet parameters', { parameters: req.params }) | 319 | logger.debug('Checking videoFileMetadataGet parameters', { parameters: req.params }) |
@@ -311,7 +326,7 @@ const videoFileMetadataGetValidator = getCommonVideoEditAttributes().concat([ | |||
311 | ]) | 326 | ]) |
312 | 327 | ||
313 | const videosRemoveValidator = [ | 328 | const videosRemoveValidator = [ |
314 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 329 | isValidVideoIdParam('id'), |
315 | 330 | ||
316 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 331 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
317 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) | 332 | logger.debug('Checking videosRemove parameters', { parameters: req.params }) |
@@ -326,74 +341,6 @@ const videosRemoveValidator = [ | |||
326 | } | 341 | } |
327 | ] | 342 | ] |
328 | 343 | ||
329 | const videosChangeOwnershipValidator = [ | ||
330 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
331 | |||
332 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
333 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
334 | |||
335 | if (areValidationErrors(req, res)) return | ||
336 | if (!await doesVideoExist(req.params.videoId, res)) return | ||
337 | |||
338 | // Check if the user who did the request is able to change the ownership of the video | ||
339 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return | ||
340 | |||
341 | const nextOwner = await AccountModel.loadLocalByName(req.body.username) | ||
342 | if (!nextOwner) { | ||
343 | res.status(HttpStatusCode.BAD_REQUEST_400) | ||
344 | .json({ error: 'Changing video ownership to a remote account is not supported yet' }) | ||
345 | |||
346 | return | ||
347 | } | ||
348 | res.locals.nextOwner = nextOwner | ||
349 | |||
350 | return next() | ||
351 | } | ||
352 | ] | ||
353 | |||
354 | const videosTerminateChangeOwnershipValidator = [ | ||
355 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
356 | |||
357 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
358 | logger.debug('Checking changeOwnership parameters', { parameters: req.params }) | ||
359 | |||
360 | if (areValidationErrors(req, res)) return | ||
361 | if (!await doesChangeVideoOwnershipExist(req.params.id, res)) return | ||
362 | |||
363 | // Check if the user who did the request is able to change the ownership of the video | ||
364 | if (!checkUserCanTerminateOwnershipChange(res.locals.oauth.token.User, res.locals.videoChangeOwnership, res)) return | ||
365 | |||
366 | const videoChangeOwnership = res.locals.videoChangeOwnership | ||
367 | |||
368 | if (videoChangeOwnership.status !== VideoChangeOwnershipStatus.WAITING) { | ||
369 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
370 | .json({ error: 'Ownership already accepted or refused' }) | ||
371 | return | ||
372 | } | ||
373 | |||
374 | return next() | ||
375 | } | ||
376 | ] | ||
377 | |||
378 | const videosAcceptChangeOwnershipValidator = [ | ||
379 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
380 | const body = req.body as VideoChangeOwnershipAccept | ||
381 | if (!await doesVideoChannelOfAccountExist(body.channelId, res.locals.oauth.token.User, res)) return | ||
382 | |||
383 | const user = res.locals.oauth.token.User | ||
384 | const videoChangeOwnership = res.locals.videoChangeOwnership | ||
385 | const isAble = await isAbleToUploadVideo(user.id, videoChangeOwnership.Video.getMaxQualityFile().size) | ||
386 | if (isAble === false) { | ||
387 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
388 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
389 | |||
390 | return | ||
391 | } | ||
392 | |||
393 | return next() | ||
394 | } | ||
395 | ] | ||
396 | |||
397 | const videosOverviewValidator = [ | 344 | const videosOverviewValidator = [ |
398 | query('page') | 345 | query('page') |
399 | .optional() | 346 | .optional() |
@@ -455,7 +402,11 @@ function getCommonVideoEditAttributes () { | |||
455 | body('tags') | 402 | body('tags') |
456 | .optional() | 403 | .optional() |
457 | .customSanitizer(toValueOrNull) | 404 | .customSanitizer(toValueOrNull) |
458 | .custom(isVideoTagsValid).withMessage('Should have correct tags'), | 405 | .custom(isVideoTagsValid) |
406 | .withMessage( | ||
407 | `Should have an array of up to ${CONSTRAINTS_FIELDS.VIDEOS.TAGS.max} tags between ` + | ||
408 | `${CONSTRAINTS_FIELDS.VIDEOS.TAG.min} and ${CONSTRAINTS_FIELDS.VIDEOS.TAG.max} characters each` | ||
409 | ), | ||
459 | body('commentsEnabled') | 410 | body('commentsEnabled') |
460 | .optional() | 411 | .optional() |
461 | .customSanitizer(toBooleanOrNull) | 412 | .customSanitizer(toBooleanOrNull) |
@@ -473,7 +424,7 @@ function getCommonVideoEditAttributes () { | |||
473 | .customSanitizer(toValueOrNull), | 424 | .customSanitizer(toValueOrNull), |
474 | body('scheduleUpdate.updateAt') | 425 | body('scheduleUpdate.updateAt') |
475 | .optional() | 426 | .optional() |
476 | .custom(isDateValid).withMessage('Should have a valid schedule update date'), | 427 | .custom(isDateValid).withMessage('Should have a schedule update date that conforms to ISO 8601'), |
477 | body('scheduleUpdate.privacy') | 428 | body('scheduleUpdate.privacy') |
478 | .optional() | 429 | .optional() |
479 | .customSanitizer(toIntOrNull) | 430 | .customSanitizer(toIntOrNull) |
@@ -530,9 +481,10 @@ const commonVideosFiltersValidator = [ | |||
530 | (req.query.filter === 'all-local' || req.query.filter === 'all') && | 481 | (req.query.filter === 'all-local' || req.query.filter === 'all') && |
531 | (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false) | 482 | (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false) |
532 | ) { | 483 | ) { |
533 | res.status(HttpStatusCode.UNAUTHORIZED_401) | 484 | res.fail({ |
534 | .json({ error: 'You are not allowed to see all local videos.' }) | 485 | status: HttpStatusCode.UNAUTHORIZED_401, |
535 | 486 | message: 'You are not allowed to see all local videos.' | |
487 | }) | ||
536 | return | 488 | return |
537 | } | 489 | } |
538 | 490 | ||
@@ -555,10 +507,6 @@ export { | |||
555 | videosCustomGetValidator, | 507 | videosCustomGetValidator, |
556 | videosRemoveValidator, | 508 | videosRemoveValidator, |
557 | 509 | ||
558 | videosChangeOwnershipValidator, | ||
559 | videosTerminateChangeOwnershipValidator, | ||
560 | videosAcceptChangeOwnershipValidator, | ||
561 | |||
562 | getCommonVideoEditAttributes, | 510 | getCommonVideoEditAttributes, |
563 | 511 | ||
564 | commonVideosFiltersValidator, | 512 | commonVideosFiltersValidator, |
@@ -573,9 +521,7 @@ function areErrorsInScheduleUpdate (req: express.Request, res: express.Response) | |||
573 | if (!req.body.scheduleUpdate.updateAt) { | 521 | if (!req.body.scheduleUpdate.updateAt) { |
574 | logger.warn('Invalid parameters: scheduleUpdate.updateAt is mandatory.') | 522 | logger.warn('Invalid parameters: scheduleUpdate.updateAt is mandatory.') |
575 | 523 | ||
576 | res.status(HttpStatusCode.BAD_REQUEST_400) | 524 | res.fail({ message: 'Schedule update at is mandatory.' }) |
577 | .json({ error: 'Schedule update at is mandatory.' }) | ||
578 | |||
579 | return true | 525 | return true |
580 | } | 526 | } |
581 | } | 527 | } |
@@ -597,26 +543,29 @@ async function commonVideoChecksPass (parameters: { | |||
597 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false | 543 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false |
598 | 544 | ||
599 | if (!isVideoFileMimeTypeValid(files)) { | 545 | if (!isVideoFileMimeTypeValid(files)) { |
600 | res.status(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) | 546 | res.fail({ |
601 | .json({ | 547 | status: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415, |
602 | error: 'This file is not supported. Please, make sure it is of the following type: ' + | 548 | message: 'This file is not supported. Please, make sure it is of the following type: ' + |
603 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') | 549 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') |
604 | }) | 550 | }) |
605 | |||
606 | return false | 551 | return false |
607 | } | 552 | } |
608 | 553 | ||
609 | if (!isVideoFileSizeValid(videoFileSize.toString())) { | 554 | if (!isVideoFileSizeValid(videoFileSize.toString())) { |
610 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | 555 | res.fail({ |
611 | .json({ error: 'This file is too large. It exceeds the maximum file size authorized.' }) | 556 | status: HttpStatusCode.PAYLOAD_TOO_LARGE_413, |
612 | 557 | message: 'This file is too large. It exceeds the maximum file size authorized.', | |
558 | type: ServerErrorCode.MAX_FILE_SIZE_REACHED | ||
559 | }) | ||
613 | return false | 560 | return false |
614 | } | 561 | } |
615 | 562 | ||
616 | if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { | 563 | if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { |
617 | res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) | 564 | res.fail({ |
618 | .json({ error: 'The user video quota is exceeded with this video.' }) | 565 | status: HttpStatusCode.PAYLOAD_TOO_LARGE_413, |
619 | 566 | message: 'The user video quota is exceeded with this video.', | |
567 | type: ServerErrorCode.QUOTA_REACHED | ||
568 | }) | ||
620 | return false | 569 | return false |
621 | } | 570 | } |
622 | 571 | ||
@@ -642,9 +591,10 @@ export async function isVideoAccepted ( | |||
642 | 591 | ||
643 | if (!acceptedResult || acceptedResult.accepted !== true) { | 592 | if (!acceptedResult || acceptedResult.accepted !== true) { |
644 | logger.info('Refused local video.', { acceptedResult, acceptParameters }) | 593 | logger.info('Refused local video.', { acceptedResult, acceptParameters }) |
645 | res.status(HttpStatusCode.FORBIDDEN_403) | 594 | res.fail({ |
646 | .json({ error: acceptedResult.errorMessage || 'Refused local video' }) | 595 | status: HttpStatusCode.FORBIDDEN_403, |
647 | 596 | message: acceptedResult.errorMessage || 'Refused local video' | |
597 | }) | ||
648 | return false | 598 | return false |
649 | } | 599 | } |
650 | 600 | ||