aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/middlewares/validators/videos
diff options
context:
space:
mode:
Diffstat (limited to 'server/middlewares/validators/videos')
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts8
-rw-r--r--server/middlewares/validators/videos/video-channels.ts47
-rw-r--r--server/middlewares/validators/videos/video-comments.ts27
-rw-r--r--server/middlewares/validators/videos/video-imports.ts23
-rw-r--r--server/middlewares/validators/videos/video-live.ts61
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts73
-rw-r--r--server/middlewares/validators/videos/video-rates.ts6
-rw-r--r--server/middlewares/validators/videos/video-watch.ts5
-rw-r--r--server/middlewares/validators/videos/videos.ts105
9 files changed, 202 insertions, 153 deletions
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 88c788a43..65132a09f 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -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()
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index e881f0d3e..331a51007 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -30,17 +30,16 @@ const videoChannelsAddValidator = [
30 30
31 const actor = await ActorModel.loadLocalByName(req.body.name) 31 const actor = await ActorModel.loadLocalByName(req.body.name)
32 if (actor) { 32 if (actor) {
33 res.status(HttpStatusCode.CONFLICT_409) 33 res.fail({
34 .send({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' }) 34 status: HttpStatusCode.CONFLICT_409,
35 .end() 35 message: 'Another actor (account/channel) with this name on this instance already exists or has already existed.'
36 })
36 return false 37 return false
37 } 38 }
38 39
39 const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) 40 const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id)
40 if (count >= VIDEO_CHANNELS.MAX_PER_USER) { 41 if (count >= VIDEO_CHANNELS.MAX_PER_USER) {
41 res.status(HttpStatusCode.BAD_REQUEST_400) 42 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 43 return false
45 } 44 }
46 45
@@ -71,13 +70,17 @@ const videoChannelsUpdateValidator = [
71 70
72 // We need to make additional checks 71 // We need to make additional checks
73 if (res.locals.videoChannel.Actor.isOwned() === false) { 72 if (res.locals.videoChannel.Actor.isOwned() === false) {
74 return res.status(HttpStatusCode.FORBIDDEN_403) 73 return res.fail({
75 .json({ error: 'Cannot update video channel of another server' }) 74 status: HttpStatusCode.FORBIDDEN_403,
75 message: 'Cannot update video channel of another server'
76 })
76 } 77 }
77 78
78 if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) { 79 if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
79 return res.status(HttpStatusCode.FORBIDDEN_403) 80 return res.fail({
80 .json({ error: 'Cannot update video channel of another user' }) 81 status: HttpStatusCode.FORBIDDEN_403,
82 message: 'Cannot update video channel of another user'
83 })
81 } 84 }
82 85
83 return next() 86 return next()
@@ -154,10 +157,10 @@ export {
154 157
155function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { 158function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) {
156 if (videoChannel.Actor.isOwned() === false) { 159 if (videoChannel.Actor.isOwned() === false) {
157 res.status(HttpStatusCode.FORBIDDEN_403) 160 res.fail({
158 .json({ error: 'Cannot remove video channel of another server.' }) 161 status: HttpStatusCode.FORBIDDEN_403,
159 .end() 162 message: 'Cannot remove video channel of another server.'
160 163 })
161 return false 164 return false
162 } 165 }
163 166
@@ -165,10 +168,10 @@ function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAcco
165 // The user can delete it if s/he is an admin 168 // The user can delete it if s/he is an admin
166 // Or if s/he is the video channel's account 169 // 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) { 170 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && videoChannel.Account.userId !== user.id) {
168 res.status(HttpStatusCode.FORBIDDEN_403) 171 res.fail({
169 .json({ error: 'Cannot remove video channel of another user' }) 172 status: HttpStatusCode.FORBIDDEN_403,
170 .end() 173 message: 'Cannot remove video channel of another user'
171 174 })
172 return false 175 return false
173 } 176 }
174 177
@@ -179,10 +182,10 @@ async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
179 const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id) 182 const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id)
180 183
181 if (count <= 1) { 184 if (count <= 1) {
182 res.status(HttpStatusCode.CONFLICT_409) 185 res.fail({
183 .json({ error: 'Cannot remove the last channel of this user' }) 186 status: HttpStatusCode.CONFLICT_409,
184 .end() 187 message: 'Cannot remove the last channel of this user'
185 188 })
186 return false 189 return false
187 } 190 }
188 191
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 1afacfed8..aac25a787 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -155,9 +155,10 @@ export {
155 155
156function isVideoCommentsEnabled (video: MVideo, res: express.Response) { 156function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
157 if (video.commentsEnabled !== true) { 157 if (video.commentsEnabled !== true) {
158 res.status(HttpStatusCode.CONFLICT_409) 158 res.fail({
159 .json({ error: 'Video comments are disabled for this video.' }) 159 status: HttpStatusCode.CONFLICT_409,
160 160 message: 'Video comments are disabled for this video.'
161 })
161 return false 162 return false
162 } 163 }
163 164
@@ -166,9 +167,10 @@ function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
166 167
167function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) { 168function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) {
168 if (videoComment.isDeleted()) { 169 if (videoComment.isDeleted()) {
169 res.status(HttpStatusCode.CONFLICT_409) 170 res.fail({
170 .json({ error: 'This comment is already deleted' }) 171 status: HttpStatusCode.CONFLICT_409,
171 172 message: 'This comment is already deleted'
173 })
172 return false 174 return false
173 } 175 }
174 176
@@ -179,9 +181,10 @@ function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MC
179 videoComment.accountId !== userAccount.id && // Not the comment owner 181 videoComment.accountId !== userAccount.id && // Not the comment owner
180 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner 182 videoComment.Video.VideoChannel.accountId !== userAccount.id // Not the video owner
181 ) { 183 ) {
182 res.status(HttpStatusCode.FORBIDDEN_403) 184 res.fail({
183 .json({ error: 'Cannot remove video comment of another user' }) 185 status: HttpStatusCode.FORBIDDEN_403,
184 186 message: 'Cannot remove video comment of another user'
187 })
185 return false 188 return false
186 } 189 }
187 190
@@ -215,9 +218,11 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon
215 218
216 if (!acceptedResult || acceptedResult.accepted !== true) { 219 if (!acceptedResult || acceptedResult.accepted !== true) {
217 logger.info('Refused local comment.', { acceptedResult, acceptParameters }) 220 logger.info('Refused local comment.', { acceptedResult, acceptParameters })
218 res.status(HttpStatusCode.FORBIDDEN_403)
219 .json({ error: acceptedResult?.errorMessage || 'Refused local comment' })
220 221
222 res.fail({
223 status: HttpStatusCode.FORBIDDEN_403,
224 message: acceptedResult?.errorMessage || 'Refused local comment'
225 })
221 return false 226 return false
222 } 227 }
223 228
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts
index a5e3ffbcd..55ff09124 100644
--- a/server/middlewares/validators/videos/video-imports.ts
+++ b/server/middlewares/validators/videos/video-imports.ts
@@ -47,14 +47,20 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([
47 47
48 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true && req.body.targetUrl) { 48 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED !== true && req.body.targetUrl) {
49 cleanUpReqFiles(req) 49 cleanUpReqFiles(req)
50 return res.status(HttpStatusCode.CONFLICT_409) 50
51 .json({ error: 'HTTP import is not enabled on this instance.' }) 51 return res.fail({
52 status: HttpStatusCode.CONFLICT_409,
53 message: 'HTTP import is not enabled on this instance.'
54 })
52 } 55 }
53 56
54 if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { 57 if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) {
55 cleanUpReqFiles(req) 58 cleanUpReqFiles(req)
56 return res.status(HttpStatusCode.CONFLICT_409) 59
57 .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) 60 return res.fail({
61 status: HttpStatusCode.CONFLICT_409,
62 message: 'Torrent/magnet URI import is not enabled on this instance.'
63 })
58 } 64 }
59 65
60 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 66 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
@@ -63,8 +69,7 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([
63 if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) { 69 if (!req.body.targetUrl && !req.body.magnetUri && !torrentFile) {
64 cleanUpReqFiles(req) 70 cleanUpReqFiles(req)
65 71
66 return res.status(HttpStatusCode.BAD_REQUEST_400) 72 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 } 73 }
69 74
70 if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) 75 if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req)
@@ -100,9 +105,11 @@ async function isImportAccepted (req: express.Request, res: express.Response) {
100 105
101 if (!acceptedResult || acceptedResult.accepted !== true) { 106 if (!acceptedResult || acceptedResult.accepted !== true) {
102 logger.info('Refused to import video.', { acceptedResult, acceptParameters }) 107 logger.info('Refused to import video.', { acceptedResult, acceptParameters })
103 res.status(HttpStatusCode.FORBIDDEN_403)
104 .json({ error: acceptedResult.errorMessage || 'Refused to import video' })
105 108
109 res.fail({
110 status: HttpStatusCode.FORBIDDEN_403,
111 message: acceptedResult.errorMessage || 'Refused to import video'
112 })
106 return false 113 return false
107 } 114 }
108 115
diff --git a/server/middlewares/validators/videos/video-live.ts b/server/middlewares/validators/videos/video-live.ts
index ec4c7f32f..9544fa4f5 100644
--- a/server/middlewares/validators/videos/video-live.ts
+++ b/server/middlewares/validators/videos/video-live.ts
@@ -30,7 +30,12 @@ const videoLiveGetValidator = [
30 if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return 30 if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return
31 31
32 const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id) 32 const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id)
33 if (!videoLive) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) 33 if (!videoLive) {
34 return res.fail({
35 status: HttpStatusCode.NOT_FOUND_404,
36 message: 'Live video not found'
37 })
38 }
34 39
35 res.locals.videoLive = videoLive 40 res.locals.videoLive = videoLive
36 41
@@ -66,22 +71,25 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([
66 if (CONFIG.LIVE.ENABLED !== true) { 71 if (CONFIG.LIVE.ENABLED !== true) {
67 cleanUpReqFiles(req) 72 cleanUpReqFiles(req)
68 73
69 return res.status(HttpStatusCode.FORBIDDEN_403) 74 return res.fail({
70 .json({ error: 'Live is not enabled on this instance' }) 75 status: HttpStatusCode.FORBIDDEN_403,
76 message: 'Live is not enabled on this instance'
77 })
71 } 78 }
72 79
73 if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { 80 if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) {
74 cleanUpReqFiles(req) 81 cleanUpReqFiles(req)
75 82
76 return res.status(HttpStatusCode.FORBIDDEN_403) 83 return res.fail({
77 .json({ error: 'Saving live replay is not allowed instance' }) 84 status: HttpStatusCode.FORBIDDEN_403,
85 message: 'Saving live replay is not allowed instance'
86 })
78 } 87 }
79 88
80 if (req.body.permanentLive && req.body.saveReplay) { 89 if (req.body.permanentLive && req.body.saveReplay) {
81 cleanUpReqFiles(req) 90 cleanUpReqFiles(req)
82 91
83 return res.status(HttpStatusCode.BAD_REQUEST_400) 92 return res.fail({ message: 'Cannot set this live as permanent while saving its replay' })
84 .json({ error: 'Cannot set this live as permanent while saving its replay' })
85 } 93 }
86 94
87 const user = res.locals.oauth.token.User 95 const user = res.locals.oauth.token.User
@@ -93,11 +101,11 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([
93 if (totalInstanceLives >= CONFIG.LIVE.MAX_INSTANCE_LIVES) { 101 if (totalInstanceLives >= CONFIG.LIVE.MAX_INSTANCE_LIVES) {
94 cleanUpReqFiles(req) 102 cleanUpReqFiles(req)
95 103
96 return res.status(HttpStatusCode.FORBIDDEN_403) 104 return res.fail({
97 .json({ 105 status: HttpStatusCode.FORBIDDEN_403,
98 code: ServerErrorCode.MAX_INSTANCE_LIVES_LIMIT_REACHED, 106 message: 'Cannot create this live because the max instance lives limit is reached.',
99 error: 'Cannot create this live because the max instance lives limit is reached.' 107 type: ServerErrorCode.MAX_INSTANCE_LIVES_LIMIT_REACHED.toString()
100 }) 108 })
101 } 109 }
102 } 110 }
103 111
@@ -107,11 +115,11 @@ const videoLiveAddValidator = getCommonVideoEditAttributes().concat([
107 if (totalUserLives >= CONFIG.LIVE.MAX_USER_LIVES) { 115 if (totalUserLives >= CONFIG.LIVE.MAX_USER_LIVES) {
108 cleanUpReqFiles(req) 116 cleanUpReqFiles(req)
109 117
110 return res.status(HttpStatusCode.FORBIDDEN_403) 118 return res.fail({
111 .json({ 119 status: HttpStatusCode.FORBIDDEN_403,
112 code: ServerErrorCode.MAX_USER_LIVES_LIMIT_REACHED, 120 type: ServerErrorCode.MAX_USER_LIVES_LIMIT_REACHED.toString(),
113 error: 'Cannot create this live because the max user lives limit is reached.' 121 message: 'Cannot create this live because the max user lives limit is reached.'
114 }) 122 })
115 } 123 }
116 } 124 }
117 125
@@ -133,18 +141,18 @@ const videoLiveUpdateValidator = [
133 if (areValidationErrors(req, res)) return 141 if (areValidationErrors(req, res)) return
134 142
135 if (req.body.permanentLive && req.body.saveReplay) { 143 if (req.body.permanentLive && req.body.saveReplay) {
136 return res.status(HttpStatusCode.BAD_REQUEST_400) 144 return res.fail({ message: 'Cannot set this live as permanent while saving its replay' })
137 .json({ error: 'Cannot set this live as permanent while saving its replay' })
138 } 145 }
139 146
140 if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) { 147 if (CONFIG.LIVE.ALLOW_REPLAY !== true && req.body.saveReplay === true) {
141 return res.status(HttpStatusCode.FORBIDDEN_403) 148 return res.fail({
142 .json({ error: 'Saving live replay is not allowed instance' }) 149 status: HttpStatusCode.FORBIDDEN_403,
150 message: 'Saving live replay is not allowed instance'
151 })
143 } 152 }
144 153
145 if (res.locals.videoAll.state !== VideoState.WAITING_FOR_LIVE) { 154 if (res.locals.videoAll.state !== VideoState.WAITING_FOR_LIVE) {
146 return res.status(HttpStatusCode.BAD_REQUEST_400) 155 return res.fail({ message: 'Cannot update a live that has already started' })
147 .json({ error: 'Cannot update a live that has already started' })
148 } 156 }
149 157
150 // Check the user can manage the live 158 // Check the user can manage the live
@@ -180,9 +188,10 @@ async function isLiveVideoAccepted (req: express.Request, res: express.Response)
180 if (!acceptedResult || acceptedResult.accepted !== true) { 188 if (!acceptedResult || acceptedResult.accepted !== true) {
181 logger.info('Refused local live video.', { acceptedResult, acceptParameters }) 189 logger.info('Refused local live video.', { acceptedResult, acceptParameters })
182 190
183 res.status(HttpStatusCode.FORBIDDEN_403) 191 res.fail({
184 .json({ error: acceptedResult.errorMessage || 'Refused local live video' }) 192 status: HttpStatusCode.FORBIDDEN_403,
185 193 message: acceptedResult.errorMessage || 'Refused local live video'
194 })
186 return false 195 return false
187 } 196 }
188 197
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index c872d045e..90815dd3a 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -46,8 +46,8 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
46 46
47 if (body.privacy === VideoPlaylistPrivacy.PUBLIC && !body.videoChannelId) { 47 if (body.privacy === VideoPlaylistPrivacy.PUBLIC && !body.videoChannelId) {
48 cleanUpReqFiles(req) 48 cleanUpReqFiles(req)
49 return res.status(HttpStatusCode.BAD_REQUEST_400) 49
50 .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' }) 50 return res.fail({ message: 'Cannot set "public" a playlist that is not assigned to a channel.' })
51 } 51 }
52 52
53 return next() 53 return next()
@@ -85,14 +85,14 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
85 ) 85 )
86 ) { 86 ) {
87 cleanUpReqFiles(req) 87 cleanUpReqFiles(req)
88 return res.status(HttpStatusCode.BAD_REQUEST_400) 88
89 .json({ error: 'Cannot set "public" a playlist that is not assigned to a channel.' }) 89 return res.fail({ message: 'Cannot set "public" a playlist that is not assigned to a channel.' })
90 } 90 }
91 91
92 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { 92 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
93 cleanUpReqFiles(req) 93 cleanUpReqFiles(req)
94 return res.status(HttpStatusCode.BAD_REQUEST_400) 94
95 .json({ error: 'Cannot update a watch later playlist.' }) 95 return res.fail({ message: 'Cannot update a watch later playlist.' })
96 } 96 }
97 97
98 if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req) 98 if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
@@ -114,8 +114,7 @@ const videoPlaylistsDeleteValidator = [
114 114
115 const videoPlaylist = getPlaylist(res) 115 const videoPlaylist = getPlaylist(res)
116 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { 116 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
117 return res.status(HttpStatusCode.BAD_REQUEST_400) 117 return res.fail({ message: 'Cannot delete a watch later playlist.' })
118 .json({ error: 'Cannot delete a watch later playlist.' })
119 } 118 }
120 119
121 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { 120 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
@@ -144,7 +143,10 @@ const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => {
144 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { 143 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
145 if (isUUIDValid(req.params.playlistId)) return next() 144 if (isUUIDValid(req.params.playlistId)) return next()
146 145
147 return res.status(HttpStatusCode.NOT_FOUND_404).end() 146 return res.fail({
147 status: HttpStatusCode.NOT_FOUND_404,
148 message: 'Playlist not found'
149 })
148 } 150 }
149 151
150 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { 152 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
@@ -156,8 +158,10 @@ const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => {
156 !user || 158 !user ||
157 (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) 159 (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST))
158 ) { 160 ) {
159 return res.status(HttpStatusCode.FORBIDDEN_403) 161 return res.fail({
160 .json({ error: 'Cannot get this private video playlist.' }) 162 status: HttpStatusCode.FORBIDDEN_403,
163 message: 'Cannot get this private video playlist.'
164 })
161 } 165 }
162 166
163 return next() 167 return next()
@@ -233,10 +237,10 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [
233 237
234 const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) 238 const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId)
235 if (!videoPlaylistElement) { 239 if (!videoPlaylistElement) {
236 res.status(HttpStatusCode.NOT_FOUND_404) 240 res.fail({
237 .json({ error: 'Video playlist element not found' }) 241 status: HttpStatusCode.NOT_FOUND_404,
238 .end() 242 message: 'Video playlist element not found'
239 243 })
240 return 244 return
241 } 245 }
242 res.locals.videoPlaylistElement = videoPlaylistElement 246 res.locals.videoPlaylistElement = videoPlaylistElement
@@ -263,15 +267,18 @@ const videoPlaylistElementAPGetValidator = [
263 267
264 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId) 268 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId)
265 if (!videoPlaylistElement) { 269 if (!videoPlaylistElement) {
266 res.status(HttpStatusCode.NOT_FOUND_404) 270 res.fail({
267 .json({ error: 'Video playlist element not found' }) 271 status: HttpStatusCode.NOT_FOUND_404,
268 .end() 272 message: 'Video playlist element not found'
269 273 })
270 return 274 return
271 } 275 }
272 276
273 if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { 277 if (videoPlaylistElement.VideoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
274 return res.status(HttpStatusCode.FORBIDDEN_403).end() 278 return res.fail({
279 status: HttpStatusCode.FORBIDDEN_403,
280 message: 'Cannot get this private video playlist.'
281 })
275 } 282 }
276 283
277 res.locals.videoPlaylistElementAP = videoPlaylistElement 284 res.locals.videoPlaylistElementAP = videoPlaylistElement
@@ -307,18 +314,12 @@ const videoPlaylistsReorderVideosValidator = [
307 const reorderLength: number = req.body.reorderLength 314 const reorderLength: number = req.body.reorderLength
308 315
309 if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) { 316 if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) {
310 res.status(HttpStatusCode.BAD_REQUEST_400) 317 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 318 return
315 } 319 }
316 320
317 if (reorderLength && reorderLength + startPosition > nextPosition) { 321 if (reorderLength && reorderLength + startPosition > nextPosition) {
318 res.status(HttpStatusCode.BAD_REQUEST_400) 322 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 323 return
323 } 324 }
324 325
@@ -401,10 +402,10 @@ function getCommonPlaylistEditAttributes () {
401 402
402function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { 403function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) {
403 if (videoPlaylist.isOwned() === false) { 404 if (videoPlaylist.isOwned() === false) {
404 res.status(HttpStatusCode.FORBIDDEN_403) 405 res.fail({
405 .json({ error: 'Cannot manage video playlist of another server.' }) 406 status: HttpStatusCode.FORBIDDEN_403,
406 .end() 407 message: 'Cannot manage video playlist of another server.'
407 408 })
408 return false 409 return false
409 } 410 }
410 411
@@ -412,10 +413,10 @@ function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: M
412 // The user can delete it if s/he is an admin 413 // The user can delete it if s/he is an admin
413 // Or if s/he is the video playlist's owner 414 // Or if s/he is the video playlist's owner
414 if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) { 415 if (user.hasRight(right) === false && videoPlaylist.ownerAccountId !== user.Account.id) {
415 res.status(HttpStatusCode.FORBIDDEN_403) 416 res.fail({
416 .json({ error: 'Cannot manage video playlist of another user' }) 417 status: HttpStatusCode.FORBIDDEN_403,
417 .end() 418 message: 'Cannot manage video playlist of another user'
418 419 })
419 return false 420 return false
420 } 421 }
421 422
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts
index 01bdef25f..5c4176f54 100644
--- a/server/middlewares/validators/videos/video-rates.ts
+++ b/server/middlewares/validators/videos/video-rates.ts
@@ -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-watch.ts b/server/middlewares/validators/videos/video-watch.ts
index 29ce0dab6..00c739d31 100644
--- a/server/middlewares/validators/videos/video-watch.ts
+++ b/server/middlewares/validators/videos/video-watch.ts
@@ -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 8864be269..dfd472400 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -73,6 +73,7 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
73 .custom(isIdValid).withMessage('Should have correct video channel id'), 73 .custom(isIdValid).withMessage('Should have correct video channel id'),
74 74
75 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 75 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
76 res.docs = "https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadLegacy"
76 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) 77 logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
77 78
78 if (areValidationErrors(req, res)) return cleanUpReqFiles(req) 79 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
@@ -88,9 +89,11 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
88 if (!videoFile.duration) await addDurationToVideo(videoFile) 89 if (!videoFile.duration) await addDurationToVideo(videoFile)
89 } catch (err) { 90 } catch (err) {
90 logger.error('Invalid input file in videosAddLegacyValidator.', { err }) 91 logger.error('Invalid input file in videosAddLegacyValidator.', { err })
91 res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422)
92 .json({ error: 'Video file unreadable.' })
93 92
93 res.fail({
94 status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
95 message: 'Video file unreadable.'
96 })
94 return cleanUpReqFiles(req) 97 return cleanUpReqFiles(req)
95 } 98 }
96 99
@@ -105,6 +108,7 @@ const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
105 */ 108 */
106const videosAddResumableValidator = [ 109const videosAddResumableValidator = [
107 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 110 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
111 res.docs = "https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadResumable"
108 const user = res.locals.oauth.token.User 112 const user = res.locals.oauth.token.User
109 113
110 const body: express.CustomUploadXFile<express.UploadXFileMetadata> = req.body 114 const body: express.CustomUploadXFile<express.UploadXFileMetadata> = req.body
@@ -118,9 +122,11 @@ const videosAddResumableValidator = [
118 if (!file.duration) await addDurationToVideo(file) 122 if (!file.duration) await addDurationToVideo(file)
119 } catch (err) { 123 } catch (err) {
120 logger.error('Invalid input file in videosAddResumableValidator.', { err }) 124 logger.error('Invalid input file in videosAddResumableValidator.', { err })
121 res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422)
122 .json({ error: 'Video file unreadable.' })
123 125
126 res.fail({
127 status: HttpStatusCode.UNPROCESSABLE_ENTITY_422,
128 message: 'Video file unreadable.'
129 })
124 return cleanup() 130 return cleanup()
125 } 131 }
126 132
@@ -164,6 +170,7 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([
164 .withMessage('Should specify the file mimetype'), 170 .withMessage('Should specify the file mimetype'),
165 171
166 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 172 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
173 res.docs = "https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadResumableInit"
167 const videoFileMetadata = { 174 const videoFileMetadata = {
168 mimetype: req.headers['x-upload-content-type'] as string, 175 mimetype: req.headers['x-upload-content-type'] as string,
169 size: +req.headers['x-upload-content-length'], 176 size: +req.headers['x-upload-content-length'],
@@ -207,6 +214,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([
207 .custom(isIdValid).withMessage('Should have correct video channel id'), 214 .custom(isIdValid).withMessage('Should have correct video channel id'),
208 215
209 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 216 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
217 res.docs = 'https://docs.joinpeertube.org/api-rest-reference.html#operation/putVideo'
210 logger.debug('Checking videosUpdate parameters', { parameters: req.body }) 218 logger.debug('Checking videosUpdate parameters', { parameters: req.body })
211 219
212 if (areValidationErrors(req, res)) return cleanUpReqFiles(req) 220 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
@@ -242,12 +250,14 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R
242 const serverActor = await getServerActor() 250 const serverActor = await getServerActor()
243 if (await VideoModel.checkVideoHasInstanceFollow(video.id, serverActor.id) === true) return next() 251 if (await VideoModel.checkVideoHasInstanceFollow(video.id, serverActor.id) === true) return next()
244 252
245 return res.status(HttpStatusCode.FORBIDDEN_403) 253 return res.fail({
246 .json({ 254 status: HttpStatusCode.FORBIDDEN_403,
247 errorCode: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS, 255 message: 'Cannot get this video regarding follow constraints.',
248 error: 'Cannot get this video regarding follow constraints.', 256 type: ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS.toString(),
249 originUrl: video.url 257 data: {
250 }) 258 originUrl: video.url
259 }
260 })
251} 261}
252 262
253const videosCustomGetValidator = ( 263const videosCustomGetValidator = (
@@ -258,6 +268,7 @@ const videosCustomGetValidator = (
258 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 268 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
259 269
260 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 270 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
271 res.docs = 'https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo'
261 logger.debug('Checking videosGet parameters', { parameters: req.params }) 272 logger.debug('Checking videosGet parameters', { parameters: req.params })
262 273
263 if (areValidationErrors(req, res)) return 274 if (areValidationErrors(req, res)) return
@@ -276,8 +287,10 @@ const videosCustomGetValidator = (
276 287
277 // Only the owner or a user that have blacklist rights can see the video 288 // Only the owner or a user that have blacklist rights can see the video
278 if (!user || !user.canGetVideo(video)) { 289 if (!user || !user.canGetVideo(video)) {
279 return res.status(HttpStatusCode.FORBIDDEN_403) 290 return res.fail({
280 .json({ error: 'Cannot get this private/internal or blacklisted video.' }) 291 status: HttpStatusCode.FORBIDDEN_403,
292 message: 'Cannot get this private/internal or blacklisted video.'
293 })
281 } 294 }
282 295
283 return next() 296 return next()
@@ -291,7 +304,10 @@ const videosCustomGetValidator = (
291 if (isUUIDValid(req.params.id)) return next() 304 if (isUUIDValid(req.params.id)) return next()
292 305
293 // Don't leak this unlisted video 306 // Don't leak this unlisted video
294 return res.status(HttpStatusCode.NOT_FOUND_404).end() 307 return res.fail({
308 status: HttpStatusCode.NOT_FOUND_404,
309 message: 'Video not found'
310 })
295 } 311 }
296 } 312 }
297 ] 313 ]
@@ -318,6 +334,7 @@ const videosRemoveValidator = [
318 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 334 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
319 335
320 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 336 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
337 res.docs = "https://docs.joinpeertube.org/api-rest-reference.html#operation/delVideo"
321 logger.debug('Checking videosRemove parameters', { parameters: req.params }) 338 logger.debug('Checking videosRemove parameters', { parameters: req.params })
322 339
323 if (areValidationErrors(req, res)) return 340 if (areValidationErrors(req, res)) return
@@ -344,13 +361,11 @@ const videosChangeOwnershipValidator = [
344 361
345 const nextOwner = await AccountModel.loadLocalByName(req.body.username) 362 const nextOwner = await AccountModel.loadLocalByName(req.body.username)
346 if (!nextOwner) { 363 if (!nextOwner) {
347 res.status(HttpStatusCode.BAD_REQUEST_400) 364 res.fail({ message: 'Changing video ownership to a remote account is not supported yet' })
348 .json({ error: 'Changing video ownership to a remote account is not supported yet' })
349
350 return 365 return
351 } 366 }
352 res.locals.nextOwner = nextOwner
353 367
368 res.locals.nextOwner = nextOwner
354 return next() 369 return next()
355 } 370 }
356] 371]
@@ -370,8 +385,10 @@ const videosTerminateChangeOwnershipValidator = [
370 const videoChangeOwnership = res.locals.videoChangeOwnership 385 const videoChangeOwnership = res.locals.videoChangeOwnership
371 386
372 if (videoChangeOwnership.status !== VideoChangeOwnershipStatus.WAITING) { 387 if (videoChangeOwnership.status !== VideoChangeOwnershipStatus.WAITING) {
373 res.status(HttpStatusCode.FORBIDDEN_403) 388 res.fail({
374 .json({ error: 'Ownership already accepted or refused' }) 389 status: HttpStatusCode.FORBIDDEN_403,
390 message: 'Ownership already accepted or refused'
391 })
375 return 392 return
376 } 393 }
377 394
@@ -388,9 +405,10 @@ const videosAcceptChangeOwnershipValidator = [
388 const videoChangeOwnership = res.locals.videoChangeOwnership 405 const videoChangeOwnership = res.locals.videoChangeOwnership
389 const isAble = await isAbleToUploadVideo(user.id, videoChangeOwnership.Video.getMaxQualityFile().size) 406 const isAble = await isAbleToUploadVideo(user.id, videoChangeOwnership.Video.getMaxQualityFile().size)
390 if (isAble === false) { 407 if (isAble === false) {
391 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) 408 res.fail({
392 .json({ error: 'The user video quota is exceeded with this video.' }) 409 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
393 410 message: 'The user video quota is exceeded with this video.'
411 })
394 return 412 return
395 } 413 }
396 414
@@ -538,9 +556,10 @@ const commonVideosFiltersValidator = [
538 (req.query.filter === 'all-local' || req.query.filter === 'all') && 556 (req.query.filter === 'all-local' || req.query.filter === 'all') &&
539 (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false) 557 (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false)
540 ) { 558 ) {
541 res.status(HttpStatusCode.UNAUTHORIZED_401) 559 res.fail({
542 .json({ error: 'You are not allowed to see all local videos.' }) 560 status: HttpStatusCode.UNAUTHORIZED_401,
543 561 message: 'You are not allowed to see all local videos.'
562 })
544 return 563 return
545 } 564 }
546 565
@@ -581,9 +600,7 @@ function areErrorsInScheduleUpdate (req: express.Request, res: express.Response)
581 if (!req.body.scheduleUpdate.updateAt) { 600 if (!req.body.scheduleUpdate.updateAt) {
582 logger.warn('Invalid parameters: scheduleUpdate.updateAt is mandatory.') 601 logger.warn('Invalid parameters: scheduleUpdate.updateAt is mandatory.')
583 602
584 res.status(HttpStatusCode.BAD_REQUEST_400) 603 res.fail({ message: 'Schedule update at is mandatory.' })
585 .json({ error: 'Schedule update at is mandatory.' })
586
587 return true 604 return true
588 } 605 }
589 } 606 }
@@ -605,26 +622,27 @@ async function commonVideoChecksPass (parameters: {
605 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false 622 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return false
606 623
607 if (!isVideoFileMimeTypeValid(files)) { 624 if (!isVideoFileMimeTypeValid(files)) {
608 res.status(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) 625 res.fail({
609 .json({ 626 status: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415,
610 error: 'This file is not supported. Please, make sure it is of the following type: ' + 627 message: 'This file is not supported. Please, make sure it is of the following type: ' +
611 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') 628 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ')
612 }) 629 })
613
614 return false 630 return false
615 } 631 }
616 632
617 if (!isVideoFileSizeValid(videoFileSize.toString())) { 633 if (!isVideoFileSizeValid(videoFileSize.toString())) {
618 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) 634 res.fail({
619 .json({ error: 'This file is too large. It exceeds the maximum file size authorized.' }) 635 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
620 636 message: 'This file is too large. It exceeds the maximum file size authorized.'
637 })
621 return false 638 return false
622 } 639 }
623 640
624 if (await isAbleToUploadVideo(user.id, videoFileSize) === false) { 641 if (await isAbleToUploadVideo(user.id, videoFileSize) === false) {
625 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413) 642 res.fail({
626 .json({ error: 'The user video quota is exceeded with this video.' }) 643 status: HttpStatusCode.PAYLOAD_TOO_LARGE_413,
627 644 message: 'The user video quota is exceeded with this video.'
645 })
628 return false 646 return false
629 } 647 }
630 648
@@ -650,9 +668,10 @@ export async function isVideoAccepted (
650 668
651 if (!acceptedResult || acceptedResult.accepted !== true) { 669 if (!acceptedResult || acceptedResult.accepted !== true) {
652 logger.info('Refused local video.', { acceptedResult, acceptParameters }) 670 logger.info('Refused local video.', { acceptedResult, acceptParameters })
653 res.status(HttpStatusCode.FORBIDDEN_403) 671 res.fail({
654 .json({ error: acceptedResult.errorMessage || 'Refused local video' }) 672 status: HttpStatusCode.FORBIDDEN_403,
655 673 message: acceptedResult.errorMessage || 'Refused local video'
674 })
656 return false 675 return false
657 } 676 }
658 677