diff options
75 files changed, 785 insertions, 547 deletions
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index 0acd44524..8034ccebf 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -431,7 +431,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
431 | .pipe( | 431 | .pipe( |
432 | // If 400, 403 or 404, the video is private or blocked so redirect to 404 | 432 | // If 400, 403 or 404, the video is private or blocked so redirect to 404 |
433 | catchError(err => { | 433 | catchError(err => { |
434 | if (err.body.errorCode === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) { | 434 | if (err.body.type === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) { |
435 | const search = window.location.search | 435 | const search = window.location.search |
436 | let originUrl = err.body.originUrl | 436 | let originUrl = err.body.originUrl |
437 | if (search) originUrl += search | 437 | if (search) originUrl += search |
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts index b8a95cca6..08ab49512 100644 --- a/client/src/app/core/rest/rest-extractor.service.ts +++ b/client/src/app/core/rest/rest-extractor.service.ts | |||
@@ -41,7 +41,7 @@ export class RestExtractor { | |||
41 | 41 | ||
42 | if (err.error instanceof Error) { | 42 | if (err.error instanceof Error) { |
43 | // A client-side or network error occurred. Handle it accordingly. | 43 | // A client-side or network error occurred. Handle it accordingly. |
44 | errorMessage = err.error.message | 44 | errorMessage = err.error.detail || err.error.title |
45 | console.error('An error occurred:', errorMessage) | 45 | console.error('An error occurred:', errorMessage) |
46 | } else if (typeof err.error === 'string') { | 46 | } else if (typeof err.error === 'string') { |
47 | errorMessage = err.error | 47 | errorMessage = err.error |
diff --git a/package.json b/package.json index 73830b605..a5a47b6c9 100644 --- a/package.json +++ b/package.json | |||
@@ -100,6 +100,7 @@ | |||
100 | "fs-extra": "^10.0.0", | 100 | "fs-extra": "^10.0.0", |
101 | "got": "^11.8.2", | 101 | "got": "^11.8.2", |
102 | "helmet": "^4.1.0", | 102 | "helmet": "^4.1.0", |
103 | "http-problem-details": "^0.1.5", | ||
103 | "http-signature": "1.3.5", | 104 | "http-signature": "1.3.5", |
104 | "ip-anonymize": "^0.1.0", | 105 | "ip-anonymize": "^0.1.0", |
105 | "ipaddr.js": "2.0.0", | 106 | "ipaddr.js": "2.0.0", |
@@ -128,6 +128,7 @@ import { LiveManager } from './server/lib/live-manager' | |||
128 | import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes' | 128 | import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes' |
129 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' | 129 | import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' |
130 | import { ServerConfigManager } from '@server/lib/server-config-manager' | 130 | import { ServerConfigManager } from '@server/lib/server-config-manager' |
131 | import { apiResponseHelpers } from '@server/helpers/express-utils' | ||
131 | 132 | ||
132 | // ----------- Command line ----------- | 133 | // ----------- Command line ----------- |
133 | 134 | ||
@@ -186,6 +187,9 @@ app.use(cookieParser()) | |||
186 | // W3C DNT Tracking Status | 187 | // W3C DNT Tracking Status |
187 | app.use(advertiseDoNotTrack) | 188 | app.use(advertiseDoNotTrack) |
188 | 189 | ||
190 | // Response helpers used in developement | ||
191 | app.use(apiResponseHelpers) | ||
192 | |||
189 | // ----------- Views, routes and static files ----------- | 193 | // ----------- Views, routes and static files ----------- |
190 | 194 | ||
191 | // API | 195 | // API |
@@ -235,7 +239,11 @@ app.use(function (err, req, res, next) { | |||
235 | const sql = err.parent ? err.parent.sql : undefined | 239 | const sql = err.parent ? err.parent.sql : undefined |
236 | 240 | ||
237 | logger.error('Error in controller.', { err: error, sql }) | 241 | logger.error('Error in controller.', { err: error, sql }) |
238 | return res.status(err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500).end() | 242 | return res.fail({ |
243 | status: err.status || HttpStatusCode.INTERNAL_SERVER_ERROR_500, | ||
244 | message: err.message, | ||
245 | type: err.name | ||
246 | }) | ||
239 | }) | 247 | }) |
240 | 248 | ||
241 | const server = createWebsocketTrackerServer(app) | 249 | const server = createWebsocketTrackerServer(app) |
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts index 0ab74bdff..108627f81 100644 --- a/server/controllers/api/abuse.ts +++ b/server/controllers/api/abuse.ts | |||
@@ -142,7 +142,7 @@ async function updateAbuse (req: express.Request, res: express.Response) { | |||
142 | 142 | ||
143 | // Do not send the delete to other instances, we updated OUR copy of this abuse | 143 | // Do not send the delete to other instances, we updated OUR copy of this abuse |
144 | 144 | ||
145 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 145 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
146 | } | 146 | } |
147 | 147 | ||
148 | async function deleteAbuse (req: express.Request, res: express.Response) { | 148 | async function deleteAbuse (req: express.Request, res: express.Response) { |
@@ -154,7 +154,7 @@ async function deleteAbuse (req: express.Request, res: express.Response) { | |||
154 | 154 | ||
155 | // Do not send the delete to other instances, we delete OUR copy of this abuse | 155 | // Do not send the delete to other instances, we delete OUR copy of this abuse |
156 | 156 | ||
157 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 157 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
158 | } | 158 | } |
159 | 159 | ||
160 | async function reportAbuse (req: express.Request, res: express.Response) { | 160 | async function reportAbuse (req: express.Request, res: express.Response) { |
@@ -244,5 +244,5 @@ async function deleteAbuseMessage (req: express.Request, res: express.Response) | |||
244 | return abuseMessage.destroy({ transaction: t }) | 244 | return abuseMessage.destroy({ transaction: t }) |
245 | }) | 245 | }) |
246 | 246 | ||
247 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 247 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
248 | } | 248 | } |
diff --git a/server/controllers/api/bulk.ts b/server/controllers/api/bulk.ts index 649351029..192daccde 100644 --- a/server/controllers/api/bulk.ts +++ b/server/controllers/api/bulk.ts | |||
@@ -34,7 +34,7 @@ async function bulkRemoveCommentsOf (req: express.Request, res: express.Response | |||
34 | const comments = await VideoCommentModel.listForBulkDelete(account, filter) | 34 | const comments = await VideoCommentModel.listForBulkDelete(account, filter) |
35 | 35 | ||
36 | // Don't wait result | 36 | // Don't wait result |
37 | res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 37 | res.status(HttpStatusCode.NO_CONTENT_204).end() |
38 | 38 | ||
39 | for (const comment of comments) { | 39 | for (const comment of comments) { |
40 | await removeComment(comment) | 40 | await removeComment(comment) |
diff --git a/server/controllers/api/custom-page.ts b/server/controllers/api/custom-page.ts index 3c47f7b9a..c19f03c56 100644 --- a/server/controllers/api/custom-page.ts +++ b/server/controllers/api/custom-page.ts | |||
@@ -27,7 +27,12 @@ export { | |||
27 | 27 | ||
28 | async function getInstanceHomepage (req: express.Request, res: express.Response) { | 28 | async function getInstanceHomepage (req: express.Request, res: express.Response) { |
29 | const page = await ActorCustomPageModel.loadInstanceHomepage() | 29 | const page = await ActorCustomPageModel.loadInstanceHomepage() |
30 | if (!page) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 30 | if (!page) { |
31 | return res.fail({ | ||
32 | status: HttpStatusCode.NOT_FOUND_404, | ||
33 | message: 'Instance homepage could not be found' | ||
34 | }) | ||
35 | } | ||
31 | 36 | ||
32 | return res.json(page.toFormattedJSON()) | 37 | return res.json(page.toFormattedJSON()) |
33 | } | 38 | } |
@@ -38,5 +43,5 @@ async function updateInstanceHomepage (req: express.Request, res: express.Respon | |||
38 | await ActorCustomPageModel.updateInstanceHomepage(content) | 43 | await ActorCustomPageModel.updateInstanceHomepage(content) |
39 | ServerConfigManager.Instance.updateHomepageState(content) | 44 | ServerConfigManager.Instance.updateHomepageState(content) |
40 | 45 | ||
41 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 46 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
42 | } | 47 | } |
diff --git a/server/controllers/api/oauth-clients.ts b/server/controllers/api/oauth-clients.ts index c21e2298d..48a10d31f 100644 --- a/server/controllers/api/oauth-clients.ts +++ b/server/controllers/api/oauth-clients.ts | |||
@@ -24,7 +24,10 @@ async function getLocalClient (req: express.Request, res: express.Response, next | |||
24 | // Don't make this check if this is a test instance | 24 | // Don't make this check if this is a test instance |
25 | if (process.env.NODE_ENV !== 'test' && req.get('host') !== headerHostShouldBe) { | 25 | if (process.env.NODE_ENV !== 'test' && req.get('host') !== headerHostShouldBe) { |
26 | logger.info('Getting client tokens for host %s is forbidden (expected %s).', req.get('host'), headerHostShouldBe) | 26 | logger.info('Getting client tokens for host %s is forbidden (expected %s).', req.get('host'), headerHostShouldBe) |
27 | return res.type('json').status(HttpStatusCode.FORBIDDEN_403).end() | 27 | return res.fail({ |
28 | status: HttpStatusCode.FORBIDDEN_403, | ||
29 | message: `Getting client tokens for host ${req.get('host')} is forbidden` | ||
30 | }) | ||
28 | } | 31 | } |
29 | 32 | ||
30 | const client = await OAuthClientModel.loadFirstClient() | 33 | const client = await OAuthClientModel.loadFirstClient() |
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index e18eed332..b64062287 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -144,7 +144,7 @@ async function installPlugin (req: express.Request, res: express.Response) { | |||
144 | return res.json(plugin.toFormattedJSON()) | 144 | return res.json(plugin.toFormattedJSON()) |
145 | } catch (err) { | 145 | } catch (err) { |
146 | logger.warn('Cannot install plugin %s.', toInstall, { err }) | 146 | logger.warn('Cannot install plugin %s.', toInstall, { err }) |
147 | return res.sendStatus(HttpStatusCode.BAD_REQUEST_400) | 147 | return res.fail({ message: 'Cannot install plugin ' + toInstall }) |
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 | ||
@@ -159,7 +159,7 @@ async function updatePlugin (req: express.Request, res: express.Response) { | |||
159 | return res.json(plugin.toFormattedJSON()) | 159 | return res.json(plugin.toFormattedJSON()) |
160 | } catch (err) { | 160 | } catch (err) { |
161 | logger.warn('Cannot update plugin %s.', toUpdate, { err }) | 161 | logger.warn('Cannot update plugin %s.', toUpdate, { err }) |
162 | return res.sendStatus(HttpStatusCode.BAD_REQUEST_400) | 162 | return res.fail({ message: 'Cannot update plugin ' + toUpdate }) |
163 | } | 163 | } |
164 | } | 164 | } |
165 | 165 | ||
@@ -168,7 +168,7 @@ async function uninstallPlugin (req: express.Request, res: express.Response) { | |||
168 | 168 | ||
169 | await PluginManager.Instance.uninstall(body.npmName) | 169 | await PluginManager.Instance.uninstall(body.npmName) |
170 | 170 | ||
171 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 171 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
172 | } | 172 | } |
173 | 173 | ||
174 | function getPublicPluginSettings (req: express.Request, res: express.Response) { | 174 | function getPublicPluginSettings (req: express.Request, res: express.Response) { |
@@ -197,7 +197,7 @@ async function updatePluginSettings (req: express.Request, res: express.Response | |||
197 | 197 | ||
198 | await PluginManager.Instance.onSettingsChanged(plugin.name, plugin.settings) | 198 | await PluginManager.Instance.onSettingsChanged(plugin.name, plugin.settings) |
199 | 199 | ||
200 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 200 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
201 | } | 201 | } |
202 | 202 | ||
203 | async function listAvailablePlugins (req: express.Request, res: express.Response) { | 203 | async function listAvailablePlugins (req: express.Request, res: express.Response) { |
@@ -206,8 +206,10 @@ async function listAvailablePlugins (req: express.Request, res: express.Response | |||
206 | const resultList = await listAvailablePluginsFromIndex(query) | 206 | const resultList = await listAvailablePluginsFromIndex(query) |
207 | 207 | ||
208 | if (!resultList) { | 208 | if (!resultList) { |
209 | return res.status(HttpStatusCode.SERVICE_UNAVAILABLE_503) | 209 | return res.fail({ |
210 | .json({ error: 'Plugin index unavailable. Please retry later' }) | 210 | status: HttpStatusCode.SERVICE_UNAVAILABLE_503, |
211 | message: 'Plugin index unavailable. Please retry later' | ||
212 | }) | ||
211 | } | 213 | } |
212 | 214 | ||
213 | return res.json(resultList) | 215 | return res.json(resultList) |
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index f0cdf3a89..77e3a024d 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -102,7 +102,10 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e | |||
102 | } catch (err) { | 102 | } catch (err) { |
103 | logger.warn('Cannot use search index to make video channels search.', { err }) | 103 | logger.warn('Cannot use search index to make video channels search.', { err }) |
104 | 104 | ||
105 | return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500) | 105 | return res.fail({ |
106 | status: HttpStatusCode.INTERNAL_SERVER_ERROR_500, | ||
107 | message: 'Cannot use search index to make video channels search' | ||
108 | }) | ||
106 | } | 109 | } |
107 | } | 110 | } |
108 | 111 | ||
@@ -202,7 +205,10 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons | |||
202 | } catch (err) { | 205 | } catch (err) { |
203 | logger.warn('Cannot use search index to make video search.', { err }) | 206 | logger.warn('Cannot use search index to make video search.', { err }) |
204 | 207 | ||
205 | return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500) | 208 | return res.fail({ |
209 | status: HttpStatusCode.INTERNAL_SERVER_ERROR_500, | ||
210 | message: 'Cannot use search index to make video search' | ||
211 | }) | ||
206 | } | 212 | } |
207 | } | 213 | } |
208 | 214 | ||
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts index ff0d9ca3c..a6e9147f3 100644 --- a/server/controllers/api/server/debug.ts +++ b/server/controllers/api/server/debug.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { InboxManager } from '@server/lib/activitypub/inbox-manager' | 1 | import { InboxManager } from '@server/lib/activitypub/inbox-manager' |
2 | import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler' | 2 | import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler' |
3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { SendDebugCommand } from '@shared/models' | 4 | import { SendDebugCommand } from '@shared/models' |
4 | import * as express from 'express' | 5 | import * as express from 'express' |
5 | import { UserRight } from '../../../../shared/models/users' | 6 | import { UserRight } from '../../../../shared/models/users' |
@@ -41,5 +42,5 @@ async function runCommand (req: express.Request, res: express.Response) { | |||
41 | await RemoveDanglingResumableUploadsScheduler.Instance.execute() | 42 | await RemoveDanglingResumableUploadsScheduler.Instance.execute() |
42 | } | 43 | } |
43 | 44 | ||
44 | return res.sendStatus(204) | 45 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
45 | } | 46 | } |
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts index 7c13dc21b..bc593ad43 100644 --- a/server/controllers/api/server/redundancy.ts +++ b/server/controllers/api/server/redundancy.ts | |||
@@ -90,13 +90,13 @@ async function addVideoRedundancy (req: express.Request, res: express.Response) | |||
90 | payload | 90 | payload |
91 | }) | 91 | }) |
92 | 92 | ||
93 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 93 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
94 | } | 94 | } |
95 | 95 | ||
96 | async function removeVideoRedundancyController (req: express.Request, res: express.Response) { | 96 | async function removeVideoRedundancyController (req: express.Request, res: express.Response) { |
97 | await removeVideoRedundancy(res.locals.videoRedundancy) | 97 | await removeVideoRedundancy(res.locals.videoRedundancy) |
98 | 98 | ||
99 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 99 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
100 | } | 100 | } |
101 | 101 | ||
102 | async function updateRedundancy (req: express.Request, res: express.Response) { | 102 | async function updateRedundancy (req: express.Request, res: express.Response) { |
@@ -110,5 +110,5 @@ async function updateRedundancy (req: express.Request, res: express.Response) { | |||
110 | removeRedundanciesOfServer(server.id) | 110 | removeRedundanciesOfServer(server.id) |
111 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err })) | 111 | .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err })) |
112 | 112 | ||
113 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 113 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
114 | } | 114 | } |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index f384f0f28..d907b49bf 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -314,7 +314,7 @@ async function removeUser (req: express.Request, res: express.Response) { | |||
314 | 314 | ||
315 | Hooks.runAction('action:api.user.deleted', { user }) | 315 | Hooks.runAction('action:api.user.deleted', { user }) |
316 | 316 | ||
317 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 317 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
318 | } | 318 | } |
319 | 319 | ||
320 | async function updateUser (req: express.Request, res: express.Response) { | 320 | async function updateUser (req: express.Request, res: express.Response) { |
@@ -349,7 +349,7 @@ async function updateUser (req: express.Request, res: express.Response) { | |||
349 | 349 | ||
350 | // Don't need to send this update to followers, these attributes are not federated | 350 | // Don't need to send this update to followers, these attributes are not federated |
351 | 351 | ||
352 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 352 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
353 | } | 353 | } |
354 | 354 | ||
355 | async function askResetUserPassword (req: express.Request, res: express.Response) { | 355 | async function askResetUserPassword (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index a609abaa6..810e4295e 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -183,7 +183,7 @@ async function deleteMe (req: express.Request, res: express.Response) { | |||
183 | 183 | ||
184 | await user.destroy() | 184 | await user.destroy() |
185 | 185 | ||
186 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 186 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
187 | } | 187 | } |
188 | 188 | ||
189 | async function updateMe (req: express.Request, res: express.Response) { | 189 | async function updateMe (req: express.Request, res: express.Response) { |
@@ -237,7 +237,7 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
237 | await sendVerifyUserEmail(user, true) | 237 | await sendVerifyUserEmail(user, true) |
238 | } | 238 | } |
239 | 239 | ||
240 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 240 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
241 | } | 241 | } |
242 | 242 | ||
243 | async function updateMyAvatar (req: express.Request, res: express.Response) { | 243 | async function updateMyAvatar (req: express.Request, res: express.Response) { |
@@ -257,5 +257,5 @@ async function deleteMyAvatar (req: express.Request, res: express.Response) { | |||
257 | const userAccount = await AccountModel.load(user.Account.id) | 257 | const userAccount = await AccountModel.load(user.Account.id) |
258 | await deleteLocalActorImageFile(userAccount, ActorImageType.AVATAR) | 258 | await deleteLocalActorImageFile(userAccount, ActorImageType.AVATAR) |
259 | 259 | ||
260 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 260 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
261 | } | 261 | } |
diff --git a/server/controllers/api/users/token.ts b/server/controllers/api/users/token.ts index 694bb0a92..863a3d74c 100644 --- a/server/controllers/api/users/token.ts +++ b/server/controllers/api/users/token.ts | |||
@@ -78,9 +78,10 @@ async function handleToken (req: express.Request, res: express.Response, next: e | |||
78 | } catch (err) { | 78 | } catch (err) { |
79 | logger.warn('Login error', { err }) | 79 | logger.warn('Login error', { err }) |
80 | 80 | ||
81 | return res.status(err.code || 400).json({ | 81 | return res.fail({ |
82 | code: err.name, | 82 | status: err.code, |
83 | error: err.message | 83 | message: err.message, |
84 | type: err.name | ||
84 | }) | 85 | }) |
85 | } | 86 | } |
86 | } | 87 | } |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 859d8b3c0..34207ea8a 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -180,7 +180,7 @@ async function deleteVideoChannelAvatar (req: express.Request, res: express.Resp | |||
180 | 180 | ||
181 | await deleteLocalActorImageFile(videoChannel, ActorImageType.AVATAR) | 181 | await deleteLocalActorImageFile(videoChannel, ActorImageType.AVATAR) |
182 | 182 | ||
183 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 183 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
184 | } | 184 | } |
185 | 185 | ||
186 | async function deleteVideoChannelBanner (req: express.Request, res: express.Response) { | 186 | async function deleteVideoChannelBanner (req: express.Request, res: express.Response) { |
@@ -188,7 +188,7 @@ async function deleteVideoChannelBanner (req: express.Request, res: express.Resp | |||
188 | 188 | ||
189 | await deleteLocalActorImageFile(videoChannel, ActorImageType.BANNER) | 189 | await deleteLocalActorImageFile(videoChannel, ActorImageType.BANNER) |
190 | 190 | ||
191 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 191 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
192 | } | 192 | } |
193 | 193 | ||
194 | async function addVideoChannel (req: express.Request, res: express.Response) { | 194 | async function addVideoChannel (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index fa8448c86..ca2b85ea5 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts | |||
@@ -70,7 +70,7 @@ async function addVideoToBlacklistController (req: express.Request, res: express | |||
70 | 70 | ||
71 | logger.info('Video %s blacklisted.', videoInstance.uuid) | 71 | logger.info('Video %s blacklisted.', videoInstance.uuid) |
72 | 72 | ||
73 | return res.type('json').sendStatus(HttpStatusCode.NO_CONTENT_204) | 73 | return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() |
74 | } | 74 | } |
75 | 75 | ||
76 | async function updateVideoBlacklistController (req: express.Request, res: express.Response) { | 76 | async function updateVideoBlacklistController (req: express.Request, res: express.Response) { |
@@ -82,7 +82,7 @@ async function updateVideoBlacklistController (req: express.Request, res: expres | |||
82 | return videoBlacklist.save({ transaction: t }) | 82 | return videoBlacklist.save({ transaction: t }) |
83 | }) | 83 | }) |
84 | 84 | ||
85 | return res.type('json').sendStatus(HttpStatusCode.NO_CONTENT_204) | 85 | return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() |
86 | } | 86 | } |
87 | 87 | ||
88 | async function listBlacklist (req: express.Request, res: express.Response) { | 88 | async function listBlacklist (req: express.Request, res: express.Response) { |
@@ -105,5 +105,5 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex | |||
105 | 105 | ||
106 | logger.info('Video %s removed from blacklist.', video.uuid) | 106 | logger.info('Video %s removed from blacklist.', video.uuid) |
107 | 107 | ||
108 | return res.type('json').sendStatus(HttpStatusCode.NO_CONTENT_204) | 108 | return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() |
109 | } | 109 | } |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index cfdf2773f..e6f28c1cb 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts | |||
@@ -166,7 +166,10 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo | |||
166 | } | 166 | } |
167 | 167 | ||
168 | if (resultList.data.length === 0) { | 168 | if (resultList.data.length === 0) { |
169 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 169 | return res.fail({ |
170 | status: HttpStatusCode.NOT_FOUND_404, | ||
171 | message: 'No comments were found' | ||
172 | }) | ||
170 | } | 173 | } |
171 | 174 | ||
172 | return res.json(buildFormattedCommentTree(resultList)) | 175 | return res.json(buildFormattedCommentTree(resultList)) |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 0d5d7a962..6ee109a8f 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -18,7 +18,6 @@ import { | |||
18 | } from '@server/types/models' | 18 | } from '@server/types/models' |
19 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' | 19 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' |
20 | import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' | 20 | import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' |
21 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
23 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 22 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
24 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 23 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
@@ -143,10 +142,12 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
143 | } catch (err) { | 142 | } catch (err) { |
144 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) | 143 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) |
145 | 144 | ||
146 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 145 | return res.fail({ |
147 | .json({ | 146 | message: 'Cannot fetch remote information of this URL.', |
148 | error: 'Cannot fetch remote information of this URL.' | 147 | data: { |
149 | }) | 148 | targetUrl |
149 | } | ||
150 | }) | ||
150 | } | 151 | } |
151 | 152 | ||
152 | const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) | 153 | const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo) |
@@ -333,12 +334,10 @@ async function processTorrentOrAbortRequest (req: express.Request, res: express. | |||
333 | if (parsedTorrent.files.length !== 1) { | 334 | if (parsedTorrent.files.length !== 1) { |
334 | cleanUpReqFiles(req) | 335 | cleanUpReqFiles(req) |
335 | 336 | ||
336 | res.status(HttpStatusCode.BAD_REQUEST_400) | 337 | res.fail({ |
337 | .json({ | 338 | type: ServerErrorCode.INCORRECT_FILES_IN_TORRENT.toString(), |
338 | code: ServerErrorCode.INCORRECT_FILES_IN_TORRENT, | 339 | message: 'Torrents with only 1 file are supported.' |
339 | error: 'Torrents with only 1 file are supported.' | 340 | }) |
340 | }) | ||
341 | |||
342 | return undefined | 341 | return undefined |
343 | } | 342 | } |
344 | 343 | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 6483d2e8a..47ab098ef 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -146,7 +146,7 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
146 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, immutableVideoAttrs.uuid) | 146 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, immutableVideoAttrs.uuid) |
147 | if (exists) { | 147 | if (exists) { |
148 | logger.debug('View for ip %s and video %s already exists.', ip, immutableVideoAttrs.uuid) | 148 | logger.debug('View for ip %s and video %s already exists.', ip, immutableVideoAttrs.uuid) |
149 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 149 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
150 | } | 150 | } |
151 | 151 | ||
152 | const video = await VideoModel.load(immutableVideoAttrs.id) | 152 | const video = await VideoModel.load(immutableVideoAttrs.id) |
@@ -179,7 +179,7 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
179 | 179 | ||
180 | Hooks.runAction('action:api.video.viewed', { video, ip }) | 180 | Hooks.runAction('action:api.video.viewed', { video, ip }) |
181 | 181 | ||
182 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 182 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
183 | } | 183 | } |
184 | 184 | ||
185 | async function getVideoDescription (req: express.Request, res: express.Response) { | 185 | async function getVideoDescription (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index 04d2494ce..6b733c577 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts | |||
@@ -76,7 +76,7 @@ async function updateLiveVideo (req: express.Request, res: express.Response) { | |||
76 | 76 | ||
77 | await federateVideoIfNeeded(video, false) | 77 | await federateVideoIfNeeded(video, false) |
78 | 78 | ||
79 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 79 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
80 | } | 80 | } |
81 | 81 | ||
82 | async function addLiveVideo (req: express.Request, res: express.Response) { | 82 | async function addLiveVideo (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index 6102f28dc..2d6ca60a8 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -122,7 +122,7 @@ function acceptOwnership (req: express.Request, res: express.Response) { | |||
122 | videoChangeOwnership.status = VideoChangeOwnershipStatus.ACCEPTED | 122 | videoChangeOwnership.status = VideoChangeOwnershipStatus.ACCEPTED |
123 | await videoChangeOwnership.save({ transaction: t }) | 123 | await videoChangeOwnership.save({ transaction: t }) |
124 | 124 | ||
125 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 125 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
126 | }) | 126 | }) |
127 | } | 127 | } |
128 | 128 | ||
@@ -133,6 +133,6 @@ function refuseOwnership (req: express.Request, res: express.Response) { | |||
133 | videoChangeOwnership.status = VideoChangeOwnershipStatus.REFUSED | 133 | videoChangeOwnership.status = VideoChangeOwnershipStatus.REFUSED |
134 | await videoChangeOwnership.save({ transaction: t }) | 134 | await videoChangeOwnership.save({ transaction: t }) |
135 | 135 | ||
136 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 136 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
137 | }) | 137 | }) |
138 | } | 138 | } |
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts index ebc17c760..c33d7fcb9 100644 --- a/server/controllers/api/videos/upload.ts +++ b/server/controllers/api/videos/upload.ts | |||
@@ -97,8 +97,11 @@ export async function addVideoLegacy (req: express.Request, res: express.Respons | |||
97 | // Uploading the video could be long | 97 | // Uploading the video could be long |
98 | // Set timeout to 10 minutes, as Express's default is 2 minutes | 98 | // Set timeout to 10 minutes, as Express's default is 2 minutes |
99 | req.setTimeout(1000 * 60 * 10, () => { | 99 | req.setTimeout(1000 * 60 * 10, () => { |
100 | logger.error('Upload video has timed out.') | 100 | logger.error('Video upload has timed out.') |
101 | return res.sendStatus(HttpStatusCode.REQUEST_TIMEOUT_408) | 101 | return res.fail({ |
102 | status: HttpStatusCode.REQUEST_TIMEOUT_408, | ||
103 | message: 'Video upload has timed out.' | ||
104 | }) | ||
102 | }) | 105 | }) |
103 | 106 | ||
104 | const videoPhysicalFile = req.files['videofile'][0] | 107 | const videoPhysicalFile = req.files['videofile'][0] |
diff --git a/server/controllers/client.ts b/server/controllers/client.ts index fcccc48e0..eb1ee6cbd 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts | |||
@@ -78,7 +78,7 @@ clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE.C | |||
78 | 78 | ||
79 | // 404 for static files not found | 79 | // 404 for static files not found |
80 | clientsRouter.use('/client/*', (req: express.Request, res: express.Response) => { | 80 | clientsRouter.use('/client/*', (req: express.Request, res: express.Response) => { |
81 | res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 81 | res.status(HttpStatusCode.NOT_FOUND_404).end() |
82 | }) | 82 | }) |
83 | 83 | ||
84 | // Always serve index client page (the client is a single page application, let it handle routing) | 84 | // Always serve index client page (the client is a single page application, let it handle routing) |
@@ -105,7 +105,7 @@ function serveServerTranslations (req: express.Request, res: express.Response) { | |||
105 | return res.sendFile(path, { maxAge: STATIC_MAX_AGE.SERVER }) | 105 | return res.sendFile(path, { maxAge: STATIC_MAX_AGE.SERVER }) |
106 | } | 106 | } |
107 | 107 | ||
108 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 108 | return res.status(HttpStatusCode.NOT_FOUND_404).end() |
109 | } | 109 | } |
110 | 110 | ||
111 | async function generateEmbedHtmlPage (req: express.Request, res: express.Response) { | 111 | async function generateEmbedHtmlPage (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/download.ts b/server/controllers/download.ts index 9a8194c5c..4293a32e2 100644 --- a/server/controllers/download.ts +++ b/server/controllers/download.ts | |||
@@ -41,7 +41,12 @@ export { | |||
41 | 41 | ||
42 | async function downloadTorrent (req: express.Request, res: express.Response) { | 42 | async function downloadTorrent (req: express.Request, res: express.Response) { |
43 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) | 43 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) |
44 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 44 | if (!result) { |
45 | return res.fail({ | ||
46 | status: HttpStatusCode.NOT_FOUND_404, | ||
47 | message: 'Torrent file not found' | ||
48 | }) | ||
49 | } | ||
45 | 50 | ||
46 | const allowParameters = { torrentPath: result.path, downloadName: result.downloadName } | 51 | const allowParameters = { torrentPath: result.path, downloadName: result.downloadName } |
47 | 52 | ||
@@ -60,7 +65,12 @@ async function downloadVideoFile (req: express.Request, res: express.Response) { | |||
60 | const video = res.locals.videoAll | 65 | const video = res.locals.videoAll |
61 | 66 | ||
62 | const videoFile = getVideoFile(req, video.VideoFiles) | 67 | const videoFile = getVideoFile(req, video.VideoFiles) |
63 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 68 | if (!videoFile) { |
69 | return res.fail({ | ||
70 | status: HttpStatusCode.NOT_FOUND_404, | ||
71 | message: 'Video file not found' | ||
72 | }) | ||
73 | } | ||
64 | 74 | ||
65 | const allowParameters = { video, videoFile } | 75 | const allowParameters = { video, videoFile } |
66 | 76 | ||
@@ -81,7 +91,12 @@ async function downloadHLSVideoFile (req: express.Request, res: express.Response | |||
81 | if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end | 91 | if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end |
82 | 92 | ||
83 | const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles) | 93 | const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles) |
84 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).end() | 94 | if (!videoFile) { |
95 | return res.fail({ | ||
96 | status: HttpStatusCode.NOT_FOUND_404, | ||
97 | message: 'Video file not found' | ||
98 | }) | ||
99 | } | ||
85 | 100 | ||
86 | const allowParameters = { video, streamingPlaylist, videoFile } | 101 | const allowParameters = { video, streamingPlaylist, videoFile } |
87 | 102 | ||
@@ -131,9 +146,11 @@ function isVideoDownloadAllowed (_object: { | |||
131 | function checkAllowResult (res: express.Response, allowParameters: any, result?: AllowedResult) { | 146 | function checkAllowResult (res: express.Response, allowParameters: any, result?: AllowedResult) { |
132 | if (!result || result.allowed !== true) { | 147 | if (!result || result.allowed !== true) { |
133 | logger.info('Download is not allowed.', { result, allowParameters }) | 148 | logger.info('Download is not allowed.', { result, allowParameters }) |
134 | res.status(HttpStatusCode.FORBIDDEN_403) | ||
135 | .json({ error: result?.errorMessage || 'Refused download' }) | ||
136 | 149 | ||
150 | res.fail({ | ||
151 | status: HttpStatusCode.FORBIDDEN_403, | ||
152 | message: result?.errorMessage || 'Refused download' | ||
153 | }) | ||
137 | return false | 154 | return false |
138 | } | 155 | } |
139 | 156 | ||
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts index 25d3b49b4..9f260cef0 100644 --- a/server/controllers/lazy-static.ts +++ b/server/controllers/lazy-static.ts | |||
@@ -56,10 +56,10 @@ async function getActorImage (req: express.Request, res: express.Response) { | |||
56 | } | 56 | } |
57 | 57 | ||
58 | const image = await ActorImageModel.loadByName(filename) | 58 | const image = await ActorImageModel.loadByName(filename) |
59 | if (!image) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 59 | if (!image) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
60 | 60 | ||
61 | if (image.onDisk === false) { | 61 | if (image.onDisk === false) { |
62 | if (!image.fileUrl) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 62 | if (!image.fileUrl) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
63 | 63 | ||
64 | logger.info('Lazy serve remote actor image %s.', image.fileUrl) | 64 | logger.info('Lazy serve remote actor image %s.', image.fileUrl) |
65 | 65 | ||
@@ -67,7 +67,7 @@ async function getActorImage (req: express.Request, res: express.Response) { | |||
67 | await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl, type: image.type }) | 67 | await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl, type: image.type }) |
68 | } catch (err) { | 68 | } catch (err) { |
69 | logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err }) | 69 | logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err }) |
70 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 70 | return res.status(HttpStatusCode.NOT_FOUND_404).end() |
71 | } | 71 | } |
72 | 72 | ||
73 | image.onDisk = true | 73 | image.onDisk = true |
@@ -83,21 +83,21 @@ async function getActorImage (req: express.Request, res: express.Response) { | |||
83 | 83 | ||
84 | async function getPreview (req: express.Request, res: express.Response) { | 84 | async function getPreview (req: express.Request, res: express.Response) { |
85 | const result = await VideosPreviewCache.Instance.getFilePath(req.params.filename) | 85 | const result = await VideosPreviewCache.Instance.getFilePath(req.params.filename) |
86 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 86 | if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
87 | 87 | ||
88 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) | 88 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) |
89 | } | 89 | } |
90 | 90 | ||
91 | async function getVideoCaption (req: express.Request, res: express.Response) { | 91 | async function getVideoCaption (req: express.Request, res: express.Response) { |
92 | const result = await VideosCaptionCache.Instance.getFilePath(req.params.filename) | 92 | const result = await VideosCaptionCache.Instance.getFilePath(req.params.filename) |
93 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 93 | if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
94 | 94 | ||
95 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) | 95 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER }) |
96 | } | 96 | } |
97 | 97 | ||
98 | async function getTorrent (req: express.Request, res: express.Response) { | 98 | async function getTorrent (req: express.Request, res: express.Response) { |
99 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) | 99 | const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename) |
100 | if (!result) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 100 | if (!result) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
101 | 101 | ||
102 | // Torrents still use the old naming convention (video uuid + .torrent) | 102 | // Torrents still use the old naming convention (video uuid + .torrent) |
103 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.SERVER }) | 103 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE.SERVER }) |
diff --git a/server/controllers/live.ts b/server/controllers/live.ts index ff48b0e21..cfb4741b7 100644 --- a/server/controllers/live.ts +++ b/server/controllers/live.ts | |||
@@ -25,7 +25,7 @@ function getSegmentsSha256 (req: express.Request, res: express.Response) { | |||
25 | const result = LiveManager.Instance.getSegmentsSha256(videoUUID) | 25 | const result = LiveManager.Instance.getSegmentsSha256(videoUUID) |
26 | 26 | ||
27 | if (!result) { | 27 | if (!result) { |
28 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 28 | return res.status(HttpStatusCode.NOT_FOUND_404).end() |
29 | } | 29 | } |
30 | 30 | ||
31 | return res.json(mapToJSON(result)) | 31 | return res.json(mapToJSON(result)) |
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts index 105f51518..7213e3f15 100644 --- a/server/controllers/plugins.ts +++ b/server/controllers/plugins.ts | |||
@@ -100,7 +100,7 @@ function getPluginTranslations (req: express.Request, res: express.Response) { | |||
100 | return res.json(json) | 100 | return res.json(json) |
101 | } | 101 | } |
102 | 102 | ||
103 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 103 | return res.status(HttpStatusCode.NOT_FOUND_404).end() |
104 | } | 104 | } |
105 | 105 | ||
106 | function servePluginStaticDirectory (req: express.Request, res: express.Response) { | 106 | function servePluginStaticDirectory (req: express.Request, res: express.Response) { |
@@ -110,7 +110,7 @@ function servePluginStaticDirectory (req: express.Request, res: express.Response | |||
110 | const [ directory, ...file ] = staticEndpoint.split('/') | 110 | const [ directory, ...file ] = staticEndpoint.split('/') |
111 | 111 | ||
112 | const staticPath = plugin.staticDirs[directory] | 112 | const staticPath = plugin.staticDirs[directory] |
113 | if (!staticPath) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 113 | if (!staticPath) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
114 | 114 | ||
115 | const filepath = file.join('/') | 115 | const filepath = file.join('/') |
116 | return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) | 116 | return res.sendFile(join(plugin.path, staticPath, filepath), sendFileOptions) |
@@ -120,7 +120,7 @@ function servePluginCustomRoutes (req: express.Request, res: express.Response, n | |||
120 | const plugin: RegisteredPlugin = res.locals.registeredPlugin | 120 | const plugin: RegisteredPlugin = res.locals.registeredPlugin |
121 | const router = PluginManager.Instance.getRouter(plugin.npmName) | 121 | const router = PluginManager.Instance.getRouter(plugin.npmName) |
122 | 122 | ||
123 | if (!router) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 123 | if (!router) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
124 | 124 | ||
125 | return router(req, res, next) | 125 | return router(req, res, next) |
126 | } | 126 | } |
@@ -130,7 +130,7 @@ function servePluginClientScripts (req: express.Request, res: express.Response) | |||
130 | const staticEndpoint = req.params.staticEndpoint | 130 | const staticEndpoint = req.params.staticEndpoint |
131 | 131 | ||
132 | const file = plugin.clientScripts[staticEndpoint] | 132 | const file = plugin.clientScripts[staticEndpoint] |
133 | if (!file) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 133 | if (!file) return res.status(HttpStatusCode.NOT_FOUND_404).end() |
134 | 134 | ||
135 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) | 135 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
136 | } | 136 | } |
@@ -140,7 +140,7 @@ function serveThemeCSSDirectory (req: express.Request, res: express.Response) { | |||
140 | const staticEndpoint = req.params.staticEndpoint | 140 | const staticEndpoint = req.params.staticEndpoint |
141 | 141 | ||
142 | if (plugin.css.includes(staticEndpoint) === false) { | 142 | if (plugin.css.includes(staticEndpoint) === false) { |
143 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 143 | return res.status(HttpStatusCode.NOT_FOUND_404).end() |
144 | } | 144 | } |
145 | 145 | ||
146 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) | 146 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 3870ebfe9..52e104346 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -160,10 +160,9 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
160 | const { totalVideos } = await VideoModel.getStats() | 160 | const { totalVideos } = await VideoModel.getStats() |
161 | const { totalLocalVideoComments } = await VideoCommentModel.getStats() | 161 | const { totalLocalVideoComments } = await VideoCommentModel.getStats() |
162 | const { totalUsers, totalMonthlyActiveUsers, totalHalfYearActiveUsers } = await UserModel.getStats() | 162 | const { totalUsers, totalMonthlyActiveUsers, totalHalfYearActiveUsers } = await UserModel.getStats() |
163 | let json = {} | ||
164 | 163 | ||
165 | if (req.params.version && (req.params.version === '2.0')) { | 164 | if (req.params.version && (req.params.version === '2.0')) { |
166 | json = { | 165 | const json = { |
167 | version: '2.0', | 166 | version: '2.0', |
168 | software: { | 167 | software: { |
169 | name: 'peertube', | 168 | name: 'peertube', |
@@ -291,12 +290,14 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
291 | } | 290 | } |
292 | } as HttpNodeinfoDiasporaSoftwareNsSchema20 | 291 | } as HttpNodeinfoDiasporaSoftwareNsSchema20 |
293 | res.contentType('application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"') | 292 | res.contentType('application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"') |
294 | } else { | 293 | .send(json) |
295 | json = { error: 'Nodeinfo schema version not handled' } | 294 | .end() |
296 | res.status(HttpStatusCode.NOT_FOUND_404) | ||
297 | } | 295 | } |
298 | 296 | ||
299 | return res.send(json).end() | 297 | return res.fail({ |
298 | status: HttpStatusCode.NOT_FOUND_404, | ||
299 | message: 'Nodeinfo schema version not handled' | ||
300 | }) | ||
300 | } | 301 | } |
301 | 302 | ||
302 | function getCup (req: express.Request, res: express.Response, next: express.NextFunction) { | 303 | function getCup (req: express.Request, res: express.Response, next: express.NextFunction) { |
diff --git a/server/helpers/custom-validators/video-comments.ts b/server/helpers/custom-validators/video-comments.ts index 8d3ce580e..5c88447ad 100644 --- a/server/helpers/custom-validators/video-comments.ts +++ b/server/helpers/custom-validators/video-comments.ts | |||
@@ -16,26 +16,20 @@ async function doesVideoCommentThreadExist (idArg: number | string, video: MVide | |||
16 | const videoComment = await VideoCommentModel.loadById(id) | 16 | const videoComment = await VideoCommentModel.loadById(id) |
17 | 17 | ||
18 | if (!videoComment) { | 18 | if (!videoComment) { |
19 | res.status(HttpStatusCode.NOT_FOUND_404) | 19 | res.fail({ |
20 | .json({ error: 'Video comment thread not found' }) | 20 | status: HttpStatusCode.NOT_FOUND_404, |
21 | .end() | 21 | message: 'Video comment thread not found' |
22 | 22 | }) | |
23 | return false | 23 | return false |
24 | } | 24 | } |
25 | 25 | ||
26 | if (videoComment.videoId !== video.id) { | 26 | if (videoComment.videoId !== video.id) { |
27 | res.status(HttpStatusCode.BAD_REQUEST_400) | 27 | res.fail({ message: 'Video comment is not associated to this video.' }) |
28 | .json({ error: 'Video comment is not associated to this video.' }) | ||
29 | .end() | ||
30 | |||
31 | return false | 28 | return false |
32 | } | 29 | } |
33 | 30 | ||
34 | if (videoComment.inReplyToCommentId !== null) { | 31 | if (videoComment.inReplyToCommentId !== null) { |
35 | res.status(HttpStatusCode.BAD_REQUEST_400) | 32 | res.fail({ message: 'Video comment is not a thread.' }) |
36 | .json({ error: 'Video comment is not a thread.' }) | ||
37 | .end() | ||
38 | |||
39 | return false | 33 | return false |
40 | } | 34 | } |
41 | 35 | ||
@@ -48,18 +42,15 @@ async function doesVideoCommentExist (idArg: number | string, video: MVideoId, r | |||
48 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) | 42 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) |
49 | 43 | ||
50 | if (!videoComment) { | 44 | if (!videoComment) { |
51 | res.status(HttpStatusCode.NOT_FOUND_404) | 45 | res.fail({ |
52 | .json({ error: 'Video comment thread not found' }) | 46 | status: HttpStatusCode.NOT_FOUND_404, |
53 | .end() | 47 | message: 'Video comment thread not found' |
54 | 48 | }) | |
55 | return false | 49 | return false |
56 | } | 50 | } |
57 | 51 | ||
58 | if (videoComment.videoId !== video.id) { | 52 | if (videoComment.videoId !== video.id) { |
59 | res.status(HttpStatusCode.BAD_REQUEST_400) | 53 | res.fail({ message: 'Video comment is not associated to this video.' }) |
60 | .json({ error: 'Video comment is not associated to this video.' }) | ||
61 | .end() | ||
62 | |||
63 | return false | 54 | return false |
64 | } | 55 | } |
65 | 56 | ||
@@ -72,14 +63,14 @@ async function doesCommentIdExist (idArg: number | string, res: express.Response | |||
72 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) | 63 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) |
73 | 64 | ||
74 | if (!videoComment) { | 65 | if (!videoComment) { |
75 | res.status(HttpStatusCode.NOT_FOUND_404) | 66 | res.fail({ |
76 | .json({ error: 'Video comment thread not found' }) | 67 | status: HttpStatusCode.NOT_FOUND_404, |
77 | 68 | message: 'Video comment thread not found' | |
69 | }) | ||
78 | return false | 70 | return false |
79 | } | 71 | } |
80 | 72 | ||
81 | res.locals.videoCommentFull = videoComment | 73 | res.locals.videoCommentFull = videoComment |
82 | |||
83 | return true | 74 | return true |
84 | } | 75 | } |
85 | 76 | ||
diff --git a/server/helpers/custom-validators/video-imports.ts b/server/helpers/custom-validators/video-imports.ts index 0063d3337..3ad7a4648 100644 --- a/server/helpers/custom-validators/video-imports.ts +++ b/server/helpers/custom-validators/video-imports.ts | |||
@@ -36,10 +36,10 @@ async function doesVideoImportExist (id: number, res: express.Response) { | |||
36 | const videoImport = await VideoImportModel.loadAndPopulateVideo(id) | 36 | const videoImport = await VideoImportModel.loadAndPopulateVideo(id) |
37 | 37 | ||
38 | if (!videoImport) { | 38 | if (!videoImport) { |
39 | res.status(HttpStatusCode.NOT_FOUND_404) | 39 | res.fail({ |
40 | .json({ error: 'Video import not found' }) | 40 | status: HttpStatusCode.NOT_FOUND_404, |
41 | .end() | 41 | message: 'Video import not found' |
42 | 42 | }) | |
43 | return false | 43 | return false |
44 | } | 44 | } |
45 | 45 | ||
diff --git a/server/helpers/custom-validators/video-ownership.ts b/server/helpers/custom-validators/video-ownership.ts index ee3cebe10..21a6b7203 100644 --- a/server/helpers/custom-validators/video-ownership.ts +++ b/server/helpers/custom-validators/video-ownership.ts | |||
@@ -9,10 +9,10 @@ export async function doesChangeVideoOwnershipExist (idArg: number | string, res | |||
9 | const videoChangeOwnership = await VideoChangeOwnershipModel.load(id) | 9 | const videoChangeOwnership = await VideoChangeOwnershipModel.load(id) |
10 | 10 | ||
11 | if (!videoChangeOwnership) { | 11 | if (!videoChangeOwnership) { |
12 | res.status(HttpStatusCode.NOT_FOUND_404) | 12 | res.fail({ |
13 | .json({ error: 'Video change ownership not found' }) | 13 | status: HttpStatusCode.NOT_FOUND_404, |
14 | .end() | 14 | message: 'Video change ownership not found' |
15 | 15 | }) | |
16 | return false | 16 | return false |
17 | } | 17 | } |
18 | 18 | ||
@@ -25,8 +25,9 @@ export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChange | |||
25 | return true | 25 | return true |
26 | } | 26 | } |
27 | 27 | ||
28 | res.status(HttpStatusCode.FORBIDDEN_403) | 28 | res.fail({ |
29 | .json({ error: 'Cannot terminate an ownership change of another user' }) | 29 | status: HttpStatusCode.FORBIDDEN_403, |
30 | .end() | 30 | message: 'Cannot terminate an ownership change of another user' |
31 | }) | ||
31 | return false | 32 | return false |
32 | } | 33 | } |
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index 010c6961a..e3ff93cdd 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts | |||
@@ -8,6 +8,7 @@ import { isArray } from './custom-validators/misc' | |||
8 | import { logger } from './logger' | 8 | import { logger } from './logger' |
9 | import { deleteFileAndCatch, generateRandomString } from './utils' | 9 | import { deleteFileAndCatch, generateRandomString } from './utils' |
10 | import { getExtFromMimetype } from './video' | 10 | import { getExtFromMimetype } from './video' |
11 | import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details' | ||
11 | 12 | ||
12 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { | 13 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { |
13 | if (paramNSFW === 'true') return true | 14 | if (paramNSFW === 'true') return true |
@@ -125,6 +126,34 @@ function getCountVideos (req: express.Request) { | |||
125 | return req.query.skipCount !== true | 126 | return req.query.skipCount !== true |
126 | } | 127 | } |
127 | 128 | ||
129 | // helpers added in server.ts and used in subsequent controllers used | ||
130 | const apiResponseHelpers = (req, res: express.Response, next = null) => { | ||
131 | res.fail = (options) => { | ||
132 | const { data, status, message, title, type, docs, instance } = { | ||
133 | data: null, | ||
134 | status: HttpStatusCode.BAD_REQUEST_400, | ||
135 | ...options | ||
136 | } | ||
137 | |||
138 | const extension = new ProblemDocumentExtension({ | ||
139 | ...data, | ||
140 | docs: docs || res.docs | ||
141 | }) | ||
142 | |||
143 | res.status(status) | ||
144 | res.setHeader('Content-Type', 'application/problem+json') | ||
145 | res.json(new ProblemDocument({ | ||
146 | status, | ||
147 | title, | ||
148 | instance, | ||
149 | type: type && '' + type, | ||
150 | detail: message | ||
151 | }, extension)) | ||
152 | } | ||
153 | |||
154 | if (next !== null) next() | ||
155 | } | ||
156 | |||
128 | // --------------------------------------------------------------------------- | 157 | // --------------------------------------------------------------------------- |
129 | 158 | ||
130 | export { | 159 | export { |
@@ -134,5 +163,6 @@ export { | |||
134 | badRequest, | 163 | badRequest, |
135 | createReqFiles, | 164 | createReqFiles, |
136 | cleanUpReqFiles, | 165 | cleanUpReqFiles, |
137 | getCountVideos | 166 | getCountVideos, |
167 | apiResponseHelpers | ||
138 | } | 168 | } |
diff --git a/server/helpers/middlewares/abuses.ts b/server/helpers/middlewares/abuses.ts index c53bd9efd..f0b1caba8 100644 --- a/server/helpers/middlewares/abuses.ts +++ b/server/helpers/middlewares/abuses.ts | |||
@@ -6,8 +6,10 @@ async function doesAbuseExist (abuseId: number | string, res: Response) { | |||
6 | const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10)) | 6 | const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10)) |
7 | 7 | ||
8 | if (!abuse) { | 8 | if (!abuse) { |
9 | res.status(HttpStatusCode.NOT_FOUND_404) | 9 | res.fail({ |
10 | .json({ error: 'Abuse not found' }) | 10 | status: HttpStatusCode.NOT_FOUND_404, |
11 | message: 'Abuse not found' | ||
12 | }) | ||
11 | 13 | ||
12 | return false | 14 | return false |
13 | } | 15 | } |
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts index 5addd3e1a..7db79bc48 100644 --- a/server/helpers/middlewares/accounts.ts +++ b/server/helpers/middlewares/accounts.ts | |||
@@ -27,15 +27,15 @@ async function doesAccountExist (p: Promise<MAccountDefault>, res: Response, sen | |||
27 | 27 | ||
28 | if (!account) { | 28 | if (!account) { |
29 | if (sendNotFound === true) { | 29 | if (sendNotFound === true) { |
30 | res.status(HttpStatusCode.NOT_FOUND_404) | 30 | res.fail({ |
31 | .json({ error: 'Account not found' }) | 31 | status: HttpStatusCode.NOT_FOUND_404, |
32 | message: 'Account not found' | ||
33 | }) | ||
32 | } | 34 | } |
33 | |||
34 | return false | 35 | return false |
35 | } | 36 | } |
36 | 37 | ||
37 | res.locals.account = account | 38 | res.locals.account = account |
38 | |||
39 | return true | 39 | return true |
40 | } | 40 | } |
41 | 41 | ||
@@ -43,14 +43,14 @@ async function doesUserFeedTokenCorrespond (id: number, token: string, res: Resp | |||
43 | const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10)) | 43 | const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10)) |
44 | 44 | ||
45 | if (token !== user.feedToken) { | 45 | if (token !== user.feedToken) { |
46 | res.status(HttpStatusCode.FORBIDDEN_403) | 46 | res.fail({ |
47 | .json({ error: 'User and token mismatch' }) | 47 | status: HttpStatusCode.FORBIDDEN_403, |
48 | 48 | message: 'User and token mismatch' | |
49 | }) | ||
49 | return false | 50 | return false |
50 | } | 51 | } |
51 | 52 | ||
52 | res.locals.user = user | 53 | res.locals.user = user |
53 | |||
54 | return true | 54 | return true |
55 | } | 55 | } |
56 | 56 | ||
diff --git a/server/helpers/middlewares/video-blacklists.ts b/server/helpers/middlewares/video-blacklists.ts index eda1324d3..3494fd6b0 100644 --- a/server/helpers/middlewares/video-blacklists.ts +++ b/server/helpers/middlewares/video-blacklists.ts | |||
@@ -6,10 +6,10 @@ async function doesVideoBlacklistExist (videoId: number, res: Response) { | |||
6 | const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId) | 6 | const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId) |
7 | 7 | ||
8 | if (videoBlacklist === null) { | 8 | if (videoBlacklist === null) { |
9 | res.status(HttpStatusCode.NOT_FOUND_404) | 9 | res.fail({ |
10 | .json({ error: 'Blacklisted video not found' }) | 10 | status: HttpStatusCode.NOT_FOUND_404, |
11 | .end() | 11 | message: 'Blacklisted video not found' |
12 | 12 | }) | |
13 | return false | 13 | return false |
14 | } | 14 | } |
15 | 15 | ||
diff --git a/server/helpers/middlewares/video-captions.ts b/server/helpers/middlewares/video-captions.ts index 226d3c5f8..2a12c4813 100644 --- a/server/helpers/middlewares/video-captions.ts +++ b/server/helpers/middlewares/video-captions.ts | |||
@@ -7,9 +7,10 @@ async function doesVideoCaptionExist (video: MVideoId, language: string, res: Re | |||
7 | const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) | 7 | const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) |
8 | 8 | ||
9 | if (!videoCaption) { | 9 | if (!videoCaption) { |
10 | res.status(HttpStatusCode.NOT_FOUND_404) | 10 | res.fail({ |
11 | .json({ error: 'Video caption not found' }) | 11 | status: HttpStatusCode.NOT_FOUND_404, |
12 | 12 | message: 'Video caption not found' | |
13 | }) | ||
13 | return false | 14 | return false |
14 | } | 15 | } |
15 | 16 | ||
diff --git a/server/helpers/middlewares/video-channels.ts b/server/helpers/middlewares/video-channels.ts index 602555921..f5ed5ef0f 100644 --- a/server/helpers/middlewares/video-channels.ts +++ b/server/helpers/middlewares/video-channels.ts | |||
@@ -31,9 +31,10 @@ export { | |||
31 | 31 | ||
32 | function processVideoChannelExist (videoChannel: MChannelBannerAccountDefault, res: express.Response) { | 32 | function processVideoChannelExist (videoChannel: MChannelBannerAccountDefault, res: express.Response) { |
33 | if (!videoChannel) { | 33 | if (!videoChannel) { |
34 | res.status(HttpStatusCode.NOT_FOUND_404) | 34 | res.fail({ |
35 | .json({ error: 'Video channel not found' }) | 35 | status: HttpStatusCode.NOT_FOUND_404, |
36 | 36 | message: 'Video channel not found' | |
37 | }) | ||
37 | return false | 38 | return false |
38 | } | 39 | } |
39 | 40 | ||
diff --git a/server/helpers/middlewares/video-playlists.ts b/server/helpers/middlewares/video-playlists.ts index d2dd80a35..3faeab677 100644 --- a/server/helpers/middlewares/video-playlists.ts +++ b/server/helpers/middlewares/video-playlists.ts | |||
@@ -28,10 +28,10 @@ export { | |||
28 | 28 | ||
29 | function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) { | 29 | function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) { |
30 | if (!videoPlaylist) { | 30 | if (!videoPlaylist) { |
31 | res.status(HttpStatusCode.NOT_FOUND_404) | 31 | res.fail({ |
32 | .json({ error: 'Video playlist not found' }) | 32 | status: HttpStatusCode.NOT_FOUND_404, |
33 | .end() | 33 | message: 'Video playlist not found' |
34 | 34 | }) | |
35 | return false | 35 | return false |
36 | } | 36 | } |
37 | 37 | ||
diff --git a/server/helpers/middlewares/videos.ts b/server/helpers/middlewares/videos.ts index 403cae092..52b934eb7 100644 --- a/server/helpers/middlewares/videos.ts +++ b/server/helpers/middlewares/videos.ts | |||
@@ -21,10 +21,10 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi | |||
21 | const video = await fetchVideo(id, fetchType, userId) | 21 | const video = await fetchVideo(id, fetchType, userId) |
22 | 22 | ||
23 | if (video === null) { | 23 | if (video === null) { |
24 | res.status(HttpStatusCode.NOT_FOUND_404) | 24 | res.fail({ |
25 | .json({ error: 'Video not found' }) | 25 | status: HttpStatusCode.NOT_FOUND_404, |
26 | .end() | 26 | message: 'Video not found' |
27 | 27 | }) | |
28 | return false | 28 | return false |
29 | } | 29 | } |
30 | 30 | ||
@@ -55,10 +55,10 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi | |||
55 | 55 | ||
56 | async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | string, res: Response) { | 56 | async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | string, res: Response) { |
57 | if (!await VideoFileModel.doesVideoExistForVideoFile(id, videoIdOrUUID)) { | 57 | if (!await VideoFileModel.doesVideoExistForVideoFile(id, videoIdOrUUID)) { |
58 | res.status(HttpStatusCode.NOT_FOUND_404) | 58 | res.fail({ |
59 | .json({ error: 'VideoFile matching Video not found' }) | 59 | status: HttpStatusCode.NOT_FOUND_404, |
60 | .end() | 60 | message: 'VideoFile matching Video not found' |
61 | 61 | }) | |
62 | return false | 62 | return false |
63 | } | 63 | } |
64 | 64 | ||
@@ -69,9 +69,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc | |||
69 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) | 69 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) |
70 | 70 | ||
71 | if (videoChannel === null) { | 71 | if (videoChannel === null) { |
72 | res.status(HttpStatusCode.BAD_REQUEST_400) | 72 | res.fail({ message: 'Unknown video "video channel" for this instance.' }) |
73 | .json({ error: 'Unknown video "video channel" for this instance.' }) | ||
74 | |||
75 | return false | 73 | return false |
76 | } | 74 | } |
77 | 75 | ||
@@ -82,9 +80,9 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc | |||
82 | } | 80 | } |
83 | 81 | ||
84 | if (videoChannel.Account.id !== user.Account.id) { | 82 | if (videoChannel.Account.id !== user.Account.id) { |
85 | res.status(HttpStatusCode.BAD_REQUEST_400) | 83 | res.fail({ |
86 | .json({ error: 'Unknown video "video channel" for this account.' }) | 84 | message: 'Unknown video "video channel" for this account.' |
87 | 85 | }) | |
88 | return false | 86 | return false |
89 | } | 87 | } |
90 | 88 | ||
@@ -95,9 +93,10 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc | |||
95 | function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response, onlyOwned = true) { | 93 | function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response, onlyOwned = true) { |
96 | // Retrieve the user who did the request | 94 | // Retrieve the user who did the request |
97 | if (onlyOwned && video.isOwned() === false) { | 95 | if (onlyOwned && video.isOwned() === false) { |
98 | res.status(HttpStatusCode.FORBIDDEN_403) | 96 | res.fail({ |
99 | .json({ error: 'Cannot manage a video of another server.' }) | 97 | status: HttpStatusCode.FORBIDDEN_403, |
100 | .end() | 98 | message: 'Cannot manage a video of another server.' |
99 | }) | ||
101 | return false | 100 | return false |
102 | } | 101 | } |
103 | 102 | ||
@@ -106,9 +105,10 @@ function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: | |||
106 | // Or if s/he is the video's account | 105 | // Or if s/he is the video's account |
107 | const account = video.VideoChannel.Account | 106 | const account = video.VideoChannel.Account |
108 | if (user.hasRight(right) === false && account.userId !== user.id) { | 107 | if (user.hasRight(right) === false && account.userId !== user.id) { |
109 | res.status(HttpStatusCode.FORBIDDEN_403) | 108 | res.fail({ |
110 | .json({ error: 'Cannot manage a video of another user.' }) | 109 | status: HttpStatusCode.FORBIDDEN_403, |
111 | .end() | 110 | message: 'Cannot manage a video of another user.' |
111 | }) | ||
112 | return false | 112 | return false |
113 | } | 113 | } |
114 | 114 | ||
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 3c09332b5..4068e3d7b 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -549,11 +549,11 @@ async function serveIndexHTML (req: express.Request, res: express.Response) { | |||
549 | return | 549 | return |
550 | } catch (err) { | 550 | } catch (err) { |
551 | logger.error('Cannot generate HTML page.', err) | 551 | logger.error('Cannot generate HTML page.', err) |
552 | return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500) | 552 | return res.status(HttpStatusCode.INTERNAL_SERVER_ERROR_500).end() |
553 | } | 553 | } |
554 | } | 554 | } |
555 | 555 | ||
556 | return res.sendStatus(HttpStatusCode.NOT_ACCEPTABLE_406) | 556 | return res.status(HttpStatusCode.NOT_ACCEPTABLE_406).end() |
557 | } | 557 | } |
558 | 558 | ||
559 | // --------------------------------------------------------------------------- | 559 | // --------------------------------------------------------------------------- |
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts index ce94a2129..6cd23f230 100644 --- a/server/middlewares/activitypub.ts +++ b/server/middlewares/activitypub.ts | |||
@@ -29,11 +29,14 @@ async function checkSignature (req: Request, res: Response, next: NextFunction) | |||
29 | const activity: ActivityDelete = req.body | 29 | const activity: ActivityDelete = req.body |
30 | if (isActorDeleteActivityValid(activity) && activity.object === activity.actor) { | 30 | if (isActorDeleteActivityValid(activity) && activity.object === activity.actor) { |
31 | logger.debug('Handling signature error on actor delete activity', { err }) | 31 | logger.debug('Handling signature error on actor delete activity', { err }) |
32 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 32 | return res.status(HttpStatusCode.NO_CONTENT_204).end() |
33 | } | 33 | } |
34 | 34 | ||
35 | logger.warn('Error in ActivityPub signature checker.', { err }) | 35 | logger.warn('Error in ActivityPub signature checker.', { err }) |
36 | return res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 36 | return res.fail({ |
37 | status: HttpStatusCode.FORBIDDEN_403, | ||
38 | message: 'ActivityPub signature could not be checked' | ||
39 | }) | ||
37 | } | 40 | } |
38 | } | 41 | } |
39 | 42 | ||
@@ -71,13 +74,22 @@ async function checkHttpSignature (req: Request, res: Response) { | |||
71 | } catch (err) { | 74 | } catch (err) { |
72 | logger.warn('Invalid signature because of exception in signature parser', { reqBody: req.body, err }) | 75 | logger.warn('Invalid signature because of exception in signature parser', { reqBody: req.body, err }) |
73 | 76 | ||
74 | res.status(HttpStatusCode.FORBIDDEN_403).json({ error: err.message }) | 77 | res.fail({ |
78 | status: HttpStatusCode.FORBIDDEN_403, | ||
79 | message: err.message | ||
80 | }) | ||
75 | return false | 81 | return false |
76 | } | 82 | } |
77 | 83 | ||
78 | const keyId = parsed.keyId | 84 | const keyId = parsed.keyId |
79 | if (!keyId) { | 85 | if (!keyId) { |
80 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 86 | res.fail({ |
87 | status: HttpStatusCode.FORBIDDEN_403, | ||
88 | message: 'Invalid key ID', | ||
89 | data: { | ||
90 | keyId | ||
91 | } | ||
92 | }) | ||
81 | return false | 93 | return false |
82 | } | 94 | } |
83 | 95 | ||
@@ -94,12 +106,17 @@ async function checkHttpSignature (req: Request, res: Response) { | |||
94 | if (verified !== true) { | 106 | if (verified !== true) { |
95 | logger.warn('Signature from %s is invalid', actorUrl, { parsed }) | 107 | logger.warn('Signature from %s is invalid', actorUrl, { parsed }) |
96 | 108 | ||
97 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 109 | res.fail({ |
110 | status: HttpStatusCode.FORBIDDEN_403, | ||
111 | message: 'Invalid signature', | ||
112 | data: { | ||
113 | actorUrl | ||
114 | } | ||
115 | }) | ||
98 | return false | 116 | return false |
99 | } | 117 | } |
100 | 118 | ||
101 | res.locals.signature = { actor } | 119 | res.locals.signature = { actor } |
102 | |||
103 | return true | 120 | return true |
104 | } | 121 | } |
105 | 122 | ||
@@ -107,7 +124,10 @@ async function checkJsonLDSignature (req: Request, res: Response) { | |||
107 | const signatureObject: ActivityPubSignature = req.body.signature | 124 | const signatureObject: ActivityPubSignature = req.body.signature |
108 | 125 | ||
109 | if (!signatureObject || !signatureObject.creator) { | 126 | if (!signatureObject || !signatureObject.creator) { |
110 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 127 | res.fail({ |
128 | status: HttpStatusCode.FORBIDDEN_403, | ||
129 | message: 'Object and creator signature do not match' | ||
130 | }) | ||
111 | return false | 131 | return false |
112 | } | 132 | } |
113 | 133 | ||
@@ -121,11 +141,13 @@ async function checkJsonLDSignature (req: Request, res: Response) { | |||
121 | if (verified !== true) { | 141 | if (verified !== true) { |
122 | logger.warn('Signature not verified.', req.body) | 142 | logger.warn('Signature not verified.', req.body) |
123 | 143 | ||
124 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 144 | res.fail({ |
145 | status: HttpStatusCode.FORBIDDEN_403, | ||
146 | message: 'Signature could not be verified' | ||
147 | }) | ||
125 | return false | 148 | return false |
126 | } | 149 | } |
127 | 150 | ||
128 | res.locals.signature = { actor } | 151 | res.locals.signature = { actor } |
129 | |||
130 | return true | 152 | return true |
131 | } | 153 | } |
diff --git a/server/middlewares/auth.ts b/server/middlewares/auth.ts index f38373624..176461cc2 100644 --- a/server/middlewares/auth.ts +++ b/server/middlewares/auth.ts | |||
@@ -16,11 +16,11 @@ function authenticate (req: express.Request, res: express.Response, next: expres | |||
16 | .catch(err => { | 16 | .catch(err => { |
17 | logger.warn('Cannot authenticate.', { err }) | 17 | logger.warn('Cannot authenticate.', { err }) |
18 | 18 | ||
19 | return res.status(err.status) | 19 | return res.fail({ |
20 | .json({ | 20 | status: err.status, |
21 | error: 'Token is invalid.', | 21 | message: 'Token is invalid', |
22 | code: err.name | 22 | type: err.name |
23 | }) | 23 | }) |
24 | }) | 24 | }) |
25 | } | 25 | } |
26 | 26 | ||
@@ -52,7 +52,12 @@ function authenticatePromiseIfNeeded (req: express.Request, res: express.Respons | |||
52 | // Already authenticated? (or tried to) | 52 | // Already authenticated? (or tried to) |
53 | if (res.locals.oauth?.token.User) return resolve() | 53 | if (res.locals.oauth?.token.User) return resolve() |
54 | 54 | ||
55 | if (res.locals.authenticated === false) return res.sendStatus(HttpStatusCode.UNAUTHORIZED_401) | 55 | if (res.locals.authenticated === false) { |
56 | return res.fail({ | ||
57 | status: HttpStatusCode.UNAUTHORIZED_401, | ||
58 | message: 'Not authenticated' | ||
59 | }) | ||
60 | } | ||
56 | 61 | ||
57 | authenticate(req, res, () => resolve(), authenticateInQuery) | 62 | authenticate(req, res, () => resolve(), authenticateInQuery) |
58 | }) | 63 | }) |
diff --git a/server/middlewares/servers.ts b/server/middlewares/servers.ts index 5e1c165f0..9aa56bc93 100644 --- a/server/middlewares/servers.ts +++ b/server/middlewares/servers.ts | |||
@@ -10,7 +10,10 @@ function setBodyHostsPort (req: express.Request, res: express.Response, next: ex | |||
10 | 10 | ||
11 | // Problem with the url parsing? | 11 | // Problem with the url parsing? |
12 | if (hostWithPort === null) { | 12 | if (hostWithPort === null) { |
13 | return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500) | 13 | return res.fail({ |
14 | status: HttpStatusCode.INTERNAL_SERVER_ERROR_500, | ||
15 | message: 'Could not parse hosts' | ||
16 | }) | ||
14 | } | 17 | } |
15 | 18 | ||
16 | req.body.hosts[i] = hostWithPort | 19 | req.body.hosts[i] = hostWithPort |
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts index 45dda4781..d1888c2d3 100644 --- a/server/middlewares/user-right.ts +++ b/server/middlewares/user-right.ts | |||
@@ -10,8 +10,10 @@ function ensureUserHasRight (userRight: UserRight) { | |||
10 | const message = `User ${user.username} does not have right ${userRight} to access to ${req.path}.` | 10 | const message = `User ${user.username} does not have right ${userRight} to access to ${req.path}.` |
11 | logger.info(message) | 11 | logger.info(message) |
12 | 12 | ||
13 | return res.status(HttpStatusCode.FORBIDDEN_403) | 13 | return res.fail({ |
14 | .json({ error: message }) | 14 | status: HttpStatusCode.FORBIDDEN_403, |
15 | message | ||
16 | }) | ||
15 | } | 17 | } |
16 | 18 | ||
17 | return next() | 19 | return next() |
diff --git a/server/middlewares/validators/abuse.ts b/server/middlewares/validators/abuse.ts index 3b897fdef..7f002e0d5 100644 --- a/server/middlewares/validators/abuse.ts +++ b/server/middlewares/validators/abuse.ts | |||
@@ -71,9 +71,7 @@ const abuseReportValidator = [ | |||
71 | if (body.comment?.id && !await doesCommentIdExist(body.comment.id, res)) return | 71 | if (body.comment?.id && !await doesCommentIdExist(body.comment.id, res)) return |
72 | 72 | ||
73 | if (!body.video?.id && !body.account?.id && !body.comment?.id) { | 73 | if (!body.video?.id && !body.account?.id && !body.comment?.id) { |
74 | res.status(HttpStatusCode.BAD_REQUEST_400) | 74 | res.fail({ message: 'video id or account id or comment id is required.' }) |
75 | .json({ error: 'video id or account id or comment id is required.' }) | ||
76 | |||
77 | return | 75 | return |
78 | } | 76 | } |
79 | 77 | ||
@@ -195,8 +193,10 @@ const getAbuseValidator = [ | |||
195 | const message = `User ${user.username} does not have right to get abuse ${abuse.id}` | 193 | const message = `User ${user.username} does not have right to get abuse ${abuse.id}` |
196 | logger.warn(message) | 194 | logger.warn(message) |
197 | 195 | ||
198 | return res.status(HttpStatusCode.FORBIDDEN_403) | 196 | return res.fail({ |
199 | .json({ error: message }) | 197 | status: HttpStatusCode.FORBIDDEN_403, |
198 | message | ||
199 | }) | ||
200 | } | 200 | } |
201 | 201 | ||
202 | return next() | 202 | return next() |
@@ -209,10 +209,7 @@ const checkAbuseValidForMessagesValidator = [ | |||
209 | 209 | ||
210 | const abuse = res.locals.abuse | 210 | const abuse = res.locals.abuse |
211 | if (abuse.ReporterAccount.isOwned() === false) { | 211 | if (abuse.ReporterAccount.isOwned() === false) { |
212 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 212 | return res.fail({ message: 'This abuse was created by a user of your instance.' }) |
213 | .json({ | ||
214 | error: 'This abuse was created by a user of your instance.' | ||
215 | }) | ||
216 | } | 213 | } |
217 | 214 | ||
218 | return next() | 215 | return next() |
@@ -246,13 +243,17 @@ const deleteAbuseMessageValidator = [ | |||
246 | const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id) | 243 | const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id) |
247 | 244 | ||
248 | if (!abuseMessage) { | 245 | if (!abuseMessage) { |
249 | return res.status(HttpStatusCode.NOT_FOUND_404) | 246 | return res.fail({ |
250 | .json({ error: 'Abuse message not found' }) | 247 | status: HttpStatusCode.NOT_FOUND_404, |
248 | message: 'Abuse message not found' | ||
249 | }) | ||
251 | } | 250 | } |
252 | 251 | ||
253 | if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuseMessage.accountId !== user.Account.id) { | 252 | if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuseMessage.accountId !== user.Account.id) { |
254 | return res.status(HttpStatusCode.FORBIDDEN_403) | 253 | return res.fail({ |
255 | .json({ error: 'Cannot delete this abuse message' }) | 254 | status: HttpStatusCode.FORBIDDEN_403, |
255 | message: 'Cannot delete this abuse message' | ||
256 | }) | ||
256 | } | 257 | } |
257 | 258 | ||
258 | res.locals.abuseMessage = abuseMessage | 259 | res.locals.abuseMessage = abuseMessage |
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts index e78ef07ef..59355e855 100644 --- a/server/middlewares/validators/activitypub/activity.ts +++ b/server/middlewares/validators/activitypub/activity.ts | |||
@@ -10,7 +10,7 @@ async function activityPubValidator (req: express.Request, res: express.Response | |||
10 | if (!isRootActivityValid(req.body)) { | 10 | if (!isRootActivityValid(req.body)) { |
11 | logger.warn('Incorrect activity parameters.', { activity: req.body }) | 11 | logger.warn('Incorrect activity parameters.', { activity: req.body }) |
12 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 12 | return res.status(HttpStatusCode.BAD_REQUEST_400) |
13 | .json({ error: 'Incorrect activity.' }) | 13 | .end() |
14 | } | 14 | } |
15 | 15 | ||
16 | const serverActor = await getServerActor() | 16 | const serverActor = await getServerActor() |
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts index f61811a1a..125ff882c 100644 --- a/server/middlewares/validators/blocklist.ts +++ b/server/middlewares/validators/blocklist.ts | |||
@@ -24,9 +24,10 @@ const blockAccountValidator = [ | |||
24 | const accountToBlock = res.locals.account | 24 | const accountToBlock = res.locals.account |
25 | 25 | ||
26 | if (user.Account.id === accountToBlock.id) { | 26 | if (user.Account.id === accountToBlock.id) { |
27 | res.status(HttpStatusCode.CONFLICT_409) | 27 | res.fail({ |
28 | .json({ error: 'You cannot block yourself.' }) | 28 | status: HttpStatusCode.CONFLICT_409, |
29 | 29 | message: 'You cannot block yourself.' | |
30 | }) | ||
30 | return | 31 | return |
31 | } | 32 | } |
32 | 33 | ||
@@ -79,8 +80,10 @@ const blockServerValidator = [ | |||
79 | const host: string = req.body.host | 80 | const host: string = req.body.host |
80 | 81 | ||
81 | if (host === WEBSERVER.HOST) { | 82 | if (host === WEBSERVER.HOST) { |
82 | return res.status(HttpStatusCode.CONFLICT_409) | 83 | return res.fail({ |
83 | .json({ error: 'You cannot block your own server.' }) | 84 | status: HttpStatusCode.CONFLICT_409, |
85 | message: 'You cannot block your own server.' | ||
86 | }) | ||
84 | } | 87 | } |
85 | 88 | ||
86 | const server = await ServerModel.loadOrCreateByHost(host) | 89 | const server = await ServerModel.loadOrCreateByHost(host) |
@@ -137,27 +140,27 @@ export { | |||
137 | async function doesUnblockAccountExist (accountId: number, targetAccountId: number, res: express.Response) { | 140 | async function doesUnblockAccountExist (accountId: number, targetAccountId: number, res: express.Response) { |
138 | const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(accountId, targetAccountId) | 141 | const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(accountId, targetAccountId) |
139 | if (!accountBlock) { | 142 | if (!accountBlock) { |
140 | res.status(HttpStatusCode.NOT_FOUND_404) | 143 | res.fail({ |
141 | .json({ error: 'Account block entry not found.' }) | 144 | status: HttpStatusCode.NOT_FOUND_404, |
142 | 145 | message: 'Account block entry not found.' | |
146 | }) | ||
143 | return false | 147 | return false |
144 | } | 148 | } |
145 | 149 | ||
146 | res.locals.accountBlock = accountBlock | 150 | res.locals.accountBlock = accountBlock |
147 | |||
148 | return true | 151 | return true |
149 | } | 152 | } |
150 | 153 | ||
151 | async function doesUnblockServerExist (accountId: number, host: string, res: express.Response) { | 154 | async function doesUnblockServerExist (accountId: number, host: string, res: express.Response) { |
152 | const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(accountId, host) | 155 | const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(accountId, host) |
153 | if (!serverBlock) { | 156 | if (!serverBlock) { |
154 | res.status(HttpStatusCode.NOT_FOUND_404) | 157 | res.fail({ |
155 | .json({ error: 'Server block entry not found.' }) | 158 | status: HttpStatusCode.NOT_FOUND_404, |
156 | 159 | message: 'Server block entry not found.' | |
160 | }) | ||
157 | return false | 161 | return false |
158 | } | 162 | } |
159 | 163 | ||
160 | res.locals.serverBlock = serverBlock | 164 | res.locals.serverBlock = serverBlock |
161 | |||
162 | return true | 165 | return true |
163 | } | 166 | } |
diff --git a/server/middlewares/validators/bulk.ts b/server/middlewares/validators/bulk.ts index cfb16d352..847885101 100644 --- a/server/middlewares/validators/bulk.ts +++ b/server/middlewares/validators/bulk.ts | |||
@@ -23,9 +23,9 @@ const bulkRemoveCommentsOfValidator = [ | |||
23 | const body = req.body as BulkRemoveCommentsOfBody | 23 | const body = req.body as BulkRemoveCommentsOfBody |
24 | 24 | ||
25 | if (body.scope === 'instance' && user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) !== true) { | 25 | if (body.scope === 'instance' && user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) !== true) { |
26 | return res.status(HttpStatusCode.FORBIDDEN_403) | 26 | return res.fail({ |
27 | .json({ | 27 | status: HttpStatusCode.FORBIDDEN_403, |
28 | error: 'User cannot remove any comments of this instance.' | 28 | message: 'User cannot remove any comments of this instance.' |
29 | }) | 29 | }) |
30 | } | 30 | } |
31 | 31 | ||
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts index e3e0c2058..b5d6b4622 100644 --- a/server/middlewares/validators/config.ts +++ b/server/middlewares/validators/config.ts | |||
@@ -2,7 +2,6 @@ import * as express from 'express' | |||
2 | import { body } from 'express-validator' | 2 | import { body } from 'express-validator' |
3 | import { isIntOrNull } from '@server/helpers/custom-validators/misc' | 3 | import { isIntOrNull } from '@server/helpers/custom-validators/misc' |
4 | import { isEmailEnabled } from '@server/initializers/config' | 4 | import { isEmailEnabled } from '@server/initializers/config' |
5 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
6 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' | 5 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' |
7 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | 6 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' |
8 | import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users' | 7 | import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users' |
@@ -115,9 +114,7 @@ function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: exp | |||
115 | if (isEmailEnabled()) return true | 114 | if (isEmailEnabled()) return true |
116 | 115 | ||
117 | if (customConfig.signup.requiresEmailVerification === true) { | 116 | if (customConfig.signup.requiresEmailVerification === true) { |
118 | res.status(HttpStatusCode.BAD_REQUEST_400) | 117 | res.fail({ message: 'Emailer is disabled but you require signup email verification.' }) |
119 | .send({ error: 'Emailer is disabled but you require signup email verification.' }) | ||
120 | .end() | ||
121 | return false | 118 | return false |
122 | } | 119 | } |
123 | 120 | ||
@@ -128,9 +125,7 @@ function checkInvalidTranscodingConfig (customConfig: CustomConfig, res: express | |||
128 | if (customConfig.transcoding.enabled === false) return true | 125 | if (customConfig.transcoding.enabled === false) return true |
129 | 126 | ||
130 | if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) { | 127 | if (customConfig.transcoding.webtorrent.enabled === false && customConfig.transcoding.hls.enabled === false) { |
131 | res.status(HttpStatusCode.BAD_REQUEST_400) | 128 | res.fail({ message: 'You need to enable at least webtorrent transcoding or hls transcoding' }) |
132 | .send({ error: 'You need to enable at least webtorrent transcoding or hls transcoding' }) | ||
133 | .end() | ||
134 | return false | 129 | return false |
135 | } | 130 | } |
136 | 131 | ||
@@ -141,9 +136,7 @@ function checkInvalidLiveConfig (customConfig: CustomConfig, res: express.Respon | |||
141 | if (customConfig.live.enabled === false) return true | 136 | if (customConfig.live.enabled === false) return true |
142 | 137 | ||
143 | if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) { | 138 | if (customConfig.live.allowReplay === true && customConfig.transcoding.enabled === false) { |
144 | res.status(HttpStatusCode.BAD_REQUEST_400) | 139 | res.fail({ message: 'You cannot allow live replay if transcoding is not enabled' }) |
145 | .send({ error: 'You cannot allow live replay if transcoding is not enabled' }) | ||
146 | .end() | ||
147 | return false | 140 | return false |
148 | } | 141 | } |
149 | 142 | ||
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts index 617661813..aa16cc993 100644 --- a/server/middlewares/validators/feeds.ts +++ b/server/middlewares/validators/feeds.ts | |||
@@ -36,10 +36,10 @@ function setFeedFormatContentType (req: express.Request, res: express.Response, | |||
36 | if (req.accepts(acceptableContentTypes)) { | 36 | if (req.accepts(acceptableContentTypes)) { |
37 | res.set('Content-Type', req.accepts(acceptableContentTypes) as string) | 37 | res.set('Content-Type', req.accepts(acceptableContentTypes) as string) |
38 | } else { | 38 | } else { |
39 | return res.status(HttpStatusCode.NOT_ACCEPTABLE_406) | 39 | return res.fail({ |
40 | .json({ | 40 | status: HttpStatusCode.NOT_ACCEPTABLE_406, |
41 | message: `You should accept at least one of the following content-types: ${acceptableContentTypes.join(', ')}` | 41 | message: `You should accept at least one of the following content-types: ${acceptableContentTypes.join(', ')}` |
42 | }) | 42 | }) |
43 | } | 43 | } |
44 | 44 | ||
45 | return next() | 45 | return next() |
@@ -106,10 +106,7 @@ const videoCommentsFeedsValidator = [ | |||
106 | if (areValidationErrors(req, res)) return | 106 | if (areValidationErrors(req, res)) return |
107 | 107 | ||
108 | if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) { | 108 | if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) { |
109 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 109 | return res.fail({ message: 'videoId cannot be mixed with a channel filter' }) |
110 | .json({ | ||
111 | message: 'videoId cannot be mixed with a channel filter' | ||
112 | }) | ||
113 | } | 110 | } |
114 | 111 | ||
115 | if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return | 112 | if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return |
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index 1d18de8cd..733be379b 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts | |||
@@ -63,11 +63,10 @@ const removeFollowingValidator = [ | |||
63 | const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, SERVER_ACTOR_NAME, req.params.host) | 63 | const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, SERVER_ACTOR_NAME, req.params.host) |
64 | 64 | ||
65 | if (!follow) { | 65 | if (!follow) { |
66 | return res | 66 | return res.fail({ |
67 | .status(HttpStatusCode.NOT_FOUND_404) | 67 | status: HttpStatusCode.NOT_FOUND_404, |
68 | .json({ | 68 | message: `Following ${req.params.host} not found.` |
69 | error: `Following ${req.params.host} not found.` | 69 | }) |
70 | }) | ||
71 | } | 70 | } |
72 | 71 | ||
73 | res.locals.follow = follow | 72 | res.locals.follow = follow |
@@ -95,12 +94,10 @@ const getFollowerValidator = [ | |||
95 | } | 94 | } |
96 | 95 | ||
97 | if (!follow) { | 96 | if (!follow) { |
98 | return res | 97 | return res.fail({ |
99 | .status(HttpStatusCode.NOT_FOUND_404) | 98 | status: HttpStatusCode.NOT_FOUND_404, |
100 | .json({ | 99 | message: `Follower ${req.params.nameWithHost} not found.` |
101 | error: `Follower ${req.params.nameWithHost} not found.` | 100 | }) |
102 | }) | ||
103 | .end() | ||
104 | } | 101 | } |
105 | 102 | ||
106 | res.locals.follow = follow | 103 | res.locals.follow = follow |
@@ -114,12 +111,7 @@ const acceptOrRejectFollowerValidator = [ | |||
114 | 111 | ||
115 | const follow = res.locals.follow | 112 | const follow = res.locals.follow |
116 | if (follow.state !== 'pending') { | 113 | if (follow.state !== 'pending') { |
117 | return res | 114 | return res.fail({ message: 'Follow is not in pending state.' }) |
118 | .status(HttpStatusCode.BAD_REQUEST_400) | ||
119 | .json({ | ||
120 | error: 'Follow is not in pending state.' | ||
121 | }) | ||
122 | .end() | ||
123 | } | 115 | } |
124 | 116 | ||
125 | return next() | 117 | return next() |
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts index 165eda6d5..b1d763fbe 100644 --- a/server/middlewares/validators/oembed.ts +++ b/server/middlewares/validators/oembed.ts | |||
@@ -51,8 +51,13 @@ const oembedValidator = [ | |||
51 | if (areValidationErrors(req, res)) return | 51 | if (areValidationErrors(req, res)) return |
52 | 52 | ||
53 | if (req.query.format !== undefined && req.query.format !== 'json') { | 53 | if (req.query.format !== undefined && req.query.format !== 'json') { |
54 | return res.status(HttpStatusCode.NOT_IMPLEMENTED_501) | 54 | return res.fail({ |
55 | .json({ error: 'Requested format is not implemented on server.' }) | 55 | status: HttpStatusCode.NOT_IMPLEMENTED_501, |
56 | message: 'Requested format is not implemented on server.', | ||
57 | data: { | ||
58 | format: req.query.format | ||
59 | } | ||
60 | }) | ||
56 | } | 61 | } |
57 | 62 | ||
58 | const url = req.query.url as string | 63 | const url = req.query.url as string |
@@ -65,27 +70,35 @@ const oembedValidator = [ | |||
65 | const matches = watchRegex.exec(url) | 70 | const matches = watchRegex.exec(url) |
66 | 71 | ||
67 | if (startIsOk === false || matches === null) { | 72 | if (startIsOk === false || matches === null) { |
68 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 73 | return res.fail({ |
69 | .json({ error: 'Invalid url.' }) | 74 | status: HttpStatusCode.BAD_REQUEST_400, |
75 | message: 'Invalid url.', | ||
76 | data: { | ||
77 | url | ||
78 | } | ||
79 | }) | ||
70 | } | 80 | } |
71 | 81 | ||
72 | const elementId = matches[1] | 82 | const elementId = matches[1] |
73 | if (isIdOrUUIDValid(elementId) === false) { | 83 | if (isIdOrUUIDValid(elementId) === false) { |
74 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 84 | return res.fail({ message: 'Invalid video or playlist id.' }) |
75 | .json({ error: 'Invalid video or playlist id.' }) | ||
76 | } | 85 | } |
77 | 86 | ||
78 | if (isVideo) { | 87 | if (isVideo) { |
79 | const video = await fetchVideo(elementId, 'all') | 88 | const video = await fetchVideo(elementId, 'all') |
80 | 89 | ||
81 | if (!video) { | 90 | if (!video) { |
82 | return res.status(HttpStatusCode.NOT_FOUND_404) | 91 | return res.fail({ |
83 | .json({ error: 'Video not found' }) | 92 | status: HttpStatusCode.NOT_FOUND_404, |
93 | message: 'Video not found' | ||
94 | }) | ||
84 | } | 95 | } |
85 | 96 | ||
86 | if (video.privacy !== VideoPrivacy.PUBLIC) { | 97 | if (video.privacy !== VideoPrivacy.PUBLIC) { |
87 | return res.status(HttpStatusCode.FORBIDDEN_403) | 98 | return res.fail({ |
88 | .json({ error: 'Video is not public' }) | 99 | status: HttpStatusCode.FORBIDDEN_403, |
100 | message: 'Video is not public' | ||
101 | }) | ||
89 | } | 102 | } |
90 | 103 | ||
91 | res.locals.videoAll = video | 104 | res.locals.videoAll = video |
@@ -96,13 +109,17 @@ const oembedValidator = [ | |||
96 | 109 | ||
97 | const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(elementId, undefined) | 110 | const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(elementId, undefined) |
98 | if (!videoPlaylist) { | 111 | if (!videoPlaylist) { |
99 | return res.status(HttpStatusCode.NOT_FOUND_404) | 112 | return res.fail({ |
100 | .json({ error: 'Video playlist not found' }) | 113 | status: HttpStatusCode.NOT_FOUND_404, |
114 | message: 'Video playlist not found' | ||
115 | }) | ||
101 | } | 116 | } |
102 | 117 | ||
103 | if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PUBLIC) { | 118 | if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PUBLIC) { |
104 | return res.status(HttpStatusCode.FORBIDDEN_403) | 119 | return res.fail({ |
105 | .json({ error: 'Playlist is not public' }) | 120 | status: HttpStatusCode.FORBIDDEN_403, |
121 | message: 'Playlist is not public' | ||
122 | }) | ||
106 | } | 123 | } |
107 | 124 | ||
108 | res.locals.videoPlaylistSummary = videoPlaylist | 125 | res.locals.videoPlaylistSummary = videoPlaylist |
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index 2c47ec5bb..5934a28bc 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts | |||
@@ -31,8 +31,18 @@ const getPluginValidator = (pluginType: PluginType, withVersion = true) => { | |||
31 | const npmName = PluginModel.buildNpmName(req.params.pluginName, pluginType) | 31 | const npmName = PluginModel.buildNpmName(req.params.pluginName, pluginType) |
32 | const plugin = PluginManager.Instance.getRegisteredPluginOrTheme(npmName) | 32 | const plugin = PluginManager.Instance.getRegisteredPluginOrTheme(npmName) |
33 | 33 | ||
34 | if (!plugin) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 34 | if (!plugin) { |
35 | if (withVersion && plugin.version !== req.params.pluginVersion) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 35 | return res.fail({ |
36 | status: HttpStatusCode.NOT_FOUND_404, | ||
37 | message: 'No plugin found named ' + npmName | ||
38 | }) | ||
39 | } | ||
40 | if (withVersion && plugin.version !== req.params.pluginVersion) { | ||
41 | return res.fail({ | ||
42 | status: HttpStatusCode.NOT_FOUND_404, | ||
43 | message: 'No plugin found named ' + npmName + ' with version ' + req.params.pluginVersion | ||
44 | }) | ||
45 | } | ||
36 | 46 | ||
37 | res.locals.registeredPlugin = plugin | 47 | res.locals.registeredPlugin = plugin |
38 | 48 | ||
@@ -50,10 +60,20 @@ const getExternalAuthValidator = [ | |||
50 | if (areValidationErrors(req, res)) return | 60 | if (areValidationErrors(req, res)) return |
51 | 61 | ||
52 | const plugin = res.locals.registeredPlugin | 62 | const plugin = res.locals.registeredPlugin |
53 | if (!plugin.registerHelpers) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 63 | if (!plugin.registerHelpers) { |
64 | return res.fail({ | ||
65 | status: HttpStatusCode.NOT_FOUND_404, | ||
66 | message: 'No registered helpers were found for this plugin' | ||
67 | }) | ||
68 | } | ||
54 | 69 | ||
55 | const externalAuth = plugin.registerHelpers.getExternalAuths().find(a => a.authName === req.params.authName) | 70 | const externalAuth = plugin.registerHelpers.getExternalAuths().find(a => a.authName === req.params.authName) |
56 | if (!externalAuth) return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 71 | if (!externalAuth) { |
72 | return res.fail({ | ||
73 | status: HttpStatusCode.NOT_FOUND_404, | ||
74 | message: 'No external auths were found for this plugin' | ||
75 | }) | ||
76 | } | ||
57 | 77 | ||
58 | res.locals.externalAuth = externalAuth | 78 | res.locals.externalAuth = externalAuth |
59 | 79 | ||
@@ -107,8 +127,7 @@ const installOrUpdatePluginValidator = [ | |||
107 | 127 | ||
108 | const body: InstallOrUpdatePlugin = req.body | 128 | const body: InstallOrUpdatePlugin = req.body |
109 | if (!body.path && !body.npmName) { | 129 | if (!body.path && !body.npmName) { |
110 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 130 | return res.fail({ message: 'Should have either a npmName or a path' }) |
111 | .json({ error: 'Should have either a npmName or a path' }) | ||
112 | } | 131 | } |
113 | 132 | ||
114 | return next() | 133 | return next() |
@@ -137,12 +156,13 @@ const existingPluginValidator = [ | |||
137 | 156 | ||
138 | const plugin = await PluginModel.loadByNpmName(req.params.npmName) | 157 | const plugin = await PluginModel.loadByNpmName(req.params.npmName) |
139 | if (!plugin) { | 158 | if (!plugin) { |
140 | return res.status(HttpStatusCode.NOT_FOUND_404) | 159 | return res.fail({ |
141 | .json({ error: 'Plugin not found' }) | 160 | status: HttpStatusCode.NOT_FOUND_404, |
161 | message: 'Plugin not found' | ||
162 | }) | ||
142 | } | 163 | } |
143 | 164 | ||
144 | res.locals.plugin = plugin | 165 | res.locals.plugin = plugin |
145 | |||
146 | return next() | 166 | return next() |
147 | } | 167 | } |
148 | ] | 168 | ] |
@@ -177,9 +197,7 @@ const listAvailablePluginsValidator = [ | |||
177 | if (areValidationErrors(req, res)) return | 197 | if (areValidationErrors(req, res)) return |
178 | 198 | ||
179 | if (CONFIG.PLUGINS.INDEX.ENABLED === false) { | 199 | if (CONFIG.PLUGINS.INDEX.ENABLED === false) { |
180 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 200 | return res.fail({ message: 'Plugin index is not enabled' }) |
181 | .json({ error: 'Plugin index is not enabled' }) | ||
182 | .end() | ||
183 | } | 201 | } |
184 | 202 | ||
185 | return next() | 203 | return next() |
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts index c379aebe4..3d557048a 100644 --- a/server/middlewares/validators/redundancy.ts +++ b/server/middlewares/validators/redundancy.ts | |||
@@ -35,11 +35,21 @@ const videoFileRedundancyGetValidator = [ | |||
35 | return f.resolution === paramResolution && (!req.params.fps || paramFPS) | 35 | return f.resolution === paramResolution && (!req.params.fps || paramFPS) |
36 | }) | 36 | }) |
37 | 37 | ||
38 | if (!videoFile) return res.status(HttpStatusCode.NOT_FOUND_404).json({ error: 'Video file not found.' }) | 38 | if (!videoFile) { |
39 | return res.fail({ | ||
40 | status: HttpStatusCode.NOT_FOUND_404, | ||
41 | message: 'Video file not found.' | ||
42 | }) | ||
43 | } | ||
39 | res.locals.videoFile = videoFile | 44 | res.locals.videoFile = videoFile |
40 | 45 | ||
41 | const videoRedundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id) | 46 | const videoRedundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id) |
42 | if (!videoRedundancy) return res.status(HttpStatusCode.NOT_FOUND_404).json({ error: 'Video redundancy not found.' }) | 47 | if (!videoRedundancy) { |
48 | return res.fail({ | ||
49 | status: HttpStatusCode.NOT_FOUND_404, | ||
50 | message: 'Video redundancy not found.' | ||
51 | }) | ||
52 | } | ||
43 | res.locals.videoRedundancy = videoRedundancy | 53 | res.locals.videoRedundancy = videoRedundancy |
44 | 54 | ||
45 | return next() | 55 | return next() |
@@ -65,11 +75,21 @@ const videoPlaylistRedundancyGetValidator = [ | |||
65 | const paramPlaylistType = req.params.streamingPlaylistType as unknown as number // We casted to int above | 75 | const paramPlaylistType = req.params.streamingPlaylistType as unknown as number // We casted to int above |
66 | const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p.type === paramPlaylistType) | 76 | const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p.type === paramPlaylistType) |
67 | 77 | ||
68 | if (!videoStreamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).json({ error: 'Video playlist not found.' }) | 78 | if (!videoStreamingPlaylist) { |
79 | return res.fail({ | ||
80 | status: HttpStatusCode.NOT_FOUND_404, | ||
81 | message: 'Video playlist not found.' | ||
82 | }) | ||
83 | } | ||
69 | res.locals.videoStreamingPlaylist = videoStreamingPlaylist | 84 | res.locals.videoStreamingPlaylist = videoStreamingPlaylist |
70 | 85 | ||
71 | const videoRedundancy = await VideoRedundancyModel.loadLocalByStreamingPlaylistId(videoStreamingPlaylist.id) | 86 | const videoRedundancy = await VideoRedundancyModel.loadLocalByStreamingPlaylistId(videoStreamingPlaylist.id) |
72 | if (!videoRedundancy) return res.status(HttpStatusCode.NOT_FOUND_404).json({ error: 'Video redundancy not found.' }) | 87 | if (!videoRedundancy) { |
88 | return res.fail({ | ||
89 | status: HttpStatusCode.NOT_FOUND_404, | ||
90 | message: 'Video redundancy not found.' | ||
91 | }) | ||
92 | } | ||
73 | res.locals.videoRedundancy = videoRedundancy | 93 | res.locals.videoRedundancy = videoRedundancy |
74 | 94 | ||
75 | return next() | 95 | return next() |
@@ -90,12 +110,10 @@ const updateServerRedundancyValidator = [ | |||
90 | const server = await ServerModel.loadByHost(req.params.host) | 110 | const server = await ServerModel.loadByHost(req.params.host) |
91 | 111 | ||
92 | if (!server) { | 112 | if (!server) { |
93 | return res | 113 | return res.fail({ |
94 | .status(HttpStatusCode.NOT_FOUND_404) | 114 | status: HttpStatusCode.NOT_FOUND_404, |
95 | .json({ | 115 | message: `Server ${req.params.host} not found.` |
96 | error: `Server ${req.params.host} not found.` | 116 | }) |
97 | }) | ||
98 | .end() | ||
99 | } | 117 | } |
100 | 118 | ||
101 | res.locals.server = server | 119 | res.locals.server = server |
@@ -129,19 +147,19 @@ const addVideoRedundancyValidator = [ | |||
129 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return | 147 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return |
130 | 148 | ||
131 | if (res.locals.onlyVideo.remote === false) { | 149 | if (res.locals.onlyVideo.remote === false) { |
132 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 150 | return res.fail({ message: 'Cannot create a redundancy on a local video' }) |
133 | .json({ error: 'Cannot create a redundancy on a local video' }) | ||
134 | } | 151 | } |
135 | 152 | ||
136 | if (res.locals.onlyVideo.isLive) { | 153 | if (res.locals.onlyVideo.isLive) { |
137 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 154 | return res.fail({ message: 'Cannot create a redundancy of a live video' }) |
138 | .json({ error: 'Cannot create a redundancy of a live video' }) | ||
139 | } | 155 | } |
140 | 156 | ||
141 | const alreadyExists = await VideoRedundancyModel.isLocalByVideoUUIDExists(res.locals.onlyVideo.uuid) | 157 | const alreadyExists = await VideoRedundancyModel.isLocalByVideoUUIDExists(res.locals.onlyVideo.uuid) |
142 | if (alreadyExists) { | 158 | if (alreadyExists) { |
143 | return res.status(HttpStatusCode.CONFLICT_409) | 159 | return res.fail({ |
144 | .json({ error: 'This video is already duplicated by your instance.' }) | 160 | status: HttpStatusCode.CONFLICT_409, |
161 | message: 'This video is already duplicated by your instance.' | ||
162 | }) | ||
145 | } | 163 | } |
146 | 164 | ||
147 | return next() | 165 | return next() |
@@ -160,9 +178,10 @@ const removeVideoRedundancyValidator = [ | |||
160 | 178 | ||
161 | const redundancy = await VideoRedundancyModel.loadByIdWithVideo(parseInt(req.params.redundancyId, 10)) | 179 | const redundancy = await VideoRedundancyModel.loadByIdWithVideo(parseInt(req.params.redundancyId, 10)) |
162 | if (!redundancy) { | 180 | if (!redundancy) { |
163 | return res.status(HttpStatusCode.NOT_FOUND_404) | 181 | return res.fail({ |
164 | .json({ error: 'Video redundancy not found' }) | 182 | status: HttpStatusCode.NOT_FOUND_404, |
165 | .end() | 183 | message: 'Video redundancy not found' |
184 | }) | ||
166 | } | 185 | } |
167 | 186 | ||
168 | res.locals.videoRedundancy = redundancy | 187 | res.locals.videoRedundancy = redundancy |
diff --git a/server/middlewares/validators/server.ts b/server/middlewares/validators/server.ts index fe6704716..2b34c4a76 100644 --- a/server/middlewares/validators/server.ts +++ b/server/middlewares/validators/server.ts | |||
@@ -19,9 +19,10 @@ const serverGetValidator = [ | |||
19 | 19 | ||
20 | const server = await ServerModel.loadByHost(req.body.host) | 20 | const server = await ServerModel.loadByHost(req.body.host) |
21 | if (!server) { | 21 | if (!server) { |
22 | return res.status(HttpStatusCode.NOT_FOUND_404) | 22 | return res.fail({ |
23 | .send({ error: 'Server host not found.' }) | 23 | status: HttpStatusCode.NOT_FOUND_404, |
24 | .end() | 24 | message: 'Server host not found.' |
25 | }) | ||
25 | } | 26 | } |
26 | 27 | ||
27 | res.locals.server = server | 28 | res.locals.server = server |
@@ -44,26 +45,26 @@ const contactAdministratorValidator = [ | |||
44 | if (areValidationErrors(req, res)) return | 45 | if (areValidationErrors(req, res)) return |
45 | 46 | ||
46 | if (CONFIG.CONTACT_FORM.ENABLED === false) { | 47 | if (CONFIG.CONTACT_FORM.ENABLED === false) { |
47 | return res | 48 | return res.fail({ |
48 | .status(HttpStatusCode.CONFLICT_409) | 49 | status: HttpStatusCode.CONFLICT_409, |
49 | .send({ error: 'Contact form is not enabled on this instance.' }) | 50 | message: 'Contact form is not enabled on this instance.' |
50 | .end() | 51 | }) |
51 | } | 52 | } |
52 | 53 | ||
53 | if (isEmailEnabled() === false) { | 54 | if (isEmailEnabled() === false) { |
54 | return res | 55 | return res.fail({ |
55 | .status(HttpStatusCode.CONFLICT_409) | 56 | status: HttpStatusCode.CONFLICT_409, |
56 | .send({ error: 'Emailer is not enabled on this instance.' }) | 57 | message: 'Emailer is not enabled on this instance.' |
57 | .end() | 58 | }) |
58 | } | 59 | } |
59 | 60 | ||
60 | if (await Redis.Instance.doesContactFormIpExist(req.ip)) { | 61 | if (await Redis.Instance.doesContactFormIpExist(req.ip)) { |
61 | logger.info('Refusing a contact form by %s: already sent one recently.', req.ip) | 62 | logger.info('Refusing a contact form by %s: already sent one recently.', req.ip) |
62 | 63 | ||
63 | return res | 64 | return res.fail({ |
64 | .status(HttpStatusCode.FORBIDDEN_403) | 65 | status: HttpStatusCode.FORBIDDEN_403, |
65 | .send({ error: 'You already sent a contact form recently.' }) | 66 | message: 'You already sent a contact form recently.' |
66 | .end() | 67 | }) |
67 | } | 68 | } |
68 | 69 | ||
69 | return next() | 70 | return next() |
diff --git a/server/middlewares/validators/themes.ts b/server/middlewares/validators/themes.ts index a726a567b..91ec0d7ac 100644 --- a/server/middlewares/validators/themes.ts +++ b/server/middlewares/validators/themes.ts | |||
@@ -20,11 +20,17 @@ const serveThemeCSSValidator = [ | |||
20 | const theme = PluginManager.Instance.getRegisteredThemeByShortName(req.params.themeName) | 20 | const theme = PluginManager.Instance.getRegisteredThemeByShortName(req.params.themeName) |
21 | 21 | ||
22 | if (!theme || theme.version !== req.params.themeVersion) { | 22 | if (!theme || theme.version !== req.params.themeVersion) { |
23 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 23 | return res.fail({ |
24 | status: HttpStatusCode.NOT_FOUND_404, | ||
25 | message: 'No theme named ' + req.params.themeName + ' was found with version ' + req.params.themeVersion | ||
26 | }) | ||
24 | } | 27 | } |
25 | 28 | ||
26 | if (theme.css.includes(req.params.staticEndpoint) === false) { | 29 | if (theme.css.includes(req.params.staticEndpoint) === false) { |
27 | return res.sendStatus(HttpStatusCode.NOT_FOUND_404) | 30 | return res.fail({ |
31 | status: HttpStatusCode.NOT_FOUND_404, | ||
32 | message: 'No static endpoint was found for this theme' | ||
33 | }) | ||
28 | } | 34 | } |
29 | 35 | ||
30 | res.locals.registeredPlugin = theme | 36 | res.locals.registeredPlugin = theme |
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts index 1823892b6..5f928b05b 100644 --- a/server/middlewares/validators/user-subscriptions.ts +++ b/server/middlewares/validators/user-subscriptions.ts | |||
@@ -61,11 +61,10 @@ const userSubscriptionGetValidator = [ | |||
61 | const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(user.Account.Actor.id, name, host) | 61 | const subscription = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(user.Account.Actor.id, name, host) |
62 | 62 | ||
63 | if (!subscription || !subscription.ActorFollowing.VideoChannel) { | 63 | if (!subscription || !subscription.ActorFollowing.VideoChannel) { |
64 | return res | 64 | return res.fail({ |
65 | .status(HttpStatusCode.NOT_FOUND_404) | 65 | status: HttpStatusCode.NOT_FOUND_404, |
66 | .json({ | 66 | message: `Subscription ${req.params.uri} not found.` |
67 | error: `Subscription ${req.params.uri} not found.` | 67 | }) |
68 | }) | ||
69 | } | 68 | } |
70 | 69 | ||
71 | res.locals.subscription = subscription | 70 | res.locals.subscription = subscription |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 548d5df4d..0eb9172c4 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -73,23 +73,23 @@ const usersAddValidator = [ | |||
73 | 73 | ||
74 | const authUser = res.locals.oauth.token.User | 74 | const authUser = res.locals.oauth.token.User |
75 | if (authUser.role !== UserRole.ADMINISTRATOR && req.body.role !== UserRole.USER) { | 75 | if (authUser.role !== UserRole.ADMINISTRATOR && req.body.role !== UserRole.USER) { |
76 | return res | 76 | return res.fail({ |
77 | .status(HttpStatusCode.FORBIDDEN_403) | 77 | status: HttpStatusCode.FORBIDDEN_403, |
78 | .json({ error: 'You can only create users (and not administrators or moderators)' }) | 78 | message: 'You can only create users (and not administrators or moderators)' |
79 | }) | ||
79 | } | 80 | } |
80 | 81 | ||
81 | if (req.body.channelName) { | 82 | if (req.body.channelName) { |
82 | if (req.body.channelName === req.body.username) { | 83 | if (req.body.channelName === req.body.username) { |
83 | return res | 84 | return res.fail({ message: 'Channel name cannot be the same as user username.' }) |
84 | .status(HttpStatusCode.BAD_REQUEST_400) | ||
85 | .json({ error: 'Channel name cannot be the same as user username.' }) | ||
86 | } | 85 | } |
87 | 86 | ||
88 | const existing = await ActorModel.loadLocalByName(req.body.channelName) | 87 | const existing = await ActorModel.loadLocalByName(req.body.channelName) |
89 | if (existing) { | 88 | if (existing) { |
90 | return res | 89 | return res.fail({ |
91 | .status(HttpStatusCode.CONFLICT_409) | 90 | status: HttpStatusCode.CONFLICT_409, |
92 | .json({ error: `Channel with name ${req.body.channelName} already exists.` }) | 91 | message: `Channel with name ${req.body.channelName} already exists.` |
92 | }) | ||
93 | } | 93 | } |
94 | } | 94 | } |
95 | 95 | ||
@@ -121,20 +121,19 @@ const usersRegisterValidator = [ | |||
121 | const body: UserRegister = req.body | 121 | const body: UserRegister = req.body |
122 | if (body.channel) { | 122 | if (body.channel) { |
123 | if (!body.channel.name || !body.channel.displayName) { | 123 | if (!body.channel.name || !body.channel.displayName) { |
124 | return res | 124 | return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' }) |
125 | .status(HttpStatusCode.BAD_REQUEST_400) | ||
126 | .json({ error: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' }) | ||
127 | } | 125 | } |
128 | 126 | ||
129 | if (body.channel.name === body.username) { | 127 | if (body.channel.name === body.username) { |
130 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 128 | return res.fail({ message: 'Channel name cannot be the same as user username.' }) |
131 | .json({ error: 'Channel name cannot be the same as user username.' }) | ||
132 | } | 129 | } |
133 | 130 | ||
134 | const existing = await ActorModel.loadLocalByName(body.channel.name) | 131 | const existing = await ActorModel.loadLocalByName(body.channel.name) |
135 | if (existing) { | 132 | if (existing) { |
136 | return res.status(HttpStatusCode.CONFLICT_409) | 133 | return res.fail({ |
137 | .json({ error: `Channel with name ${body.channel.name} already exists.` }) | 134 | status: HttpStatusCode.CONFLICT_409, |
135 | message: `Channel with name ${body.channel.name} already exists.` | ||
136 | }) | ||
138 | } | 137 | } |
139 | } | 138 | } |
140 | 139 | ||
@@ -153,8 +152,7 @@ const usersRemoveValidator = [ | |||
153 | 152 | ||
154 | const user = res.locals.user | 153 | const user = res.locals.user |
155 | if (user.username === 'root') { | 154 | if (user.username === 'root') { |
156 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 155 | return res.fail({ message: 'Cannot remove the root user' }) |
157 | .json({ error: 'Cannot remove the root user' }) | ||
158 | } | 156 | } |
159 | 157 | ||
160 | return next() | 158 | return next() |
@@ -173,8 +171,7 @@ const usersBlockingValidator = [ | |||
173 | 171 | ||
174 | const user = res.locals.user | 172 | const user = res.locals.user |
175 | if (user.username === 'root') { | 173 | if (user.username === 'root') { |
176 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 174 | return res.fail({ message: 'Cannot block the root user' }) |
177 | .json({ error: 'Cannot block the root user' }) | ||
178 | } | 175 | } |
179 | 176 | ||
180 | return next() | 177 | return next() |
@@ -185,9 +182,7 @@ const deleteMeValidator = [ | |||
185 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 182 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
186 | const user = res.locals.oauth.token.User | 183 | const user = res.locals.oauth.token.User |
187 | if (user.username === 'root') { | 184 | if (user.username === 'root') { |
188 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 185 | return res.fail({ message: 'You cannot delete your root account.' }) |
189 | .json({ error: 'You cannot delete your root account.' }) | ||
190 | .end() | ||
191 | } | 186 | } |
192 | 187 | ||
193 | return next() | 188 | return next() |
@@ -217,8 +212,7 @@ const usersUpdateValidator = [ | |||
217 | 212 | ||
218 | const user = res.locals.user | 213 | const user = res.locals.user |
219 | if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) { | 214 | if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) { |
220 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 215 | return res.fail({ message: 'Cannot change root role.' }) |
221 | .json({ error: 'Cannot change root role.' }) | ||
222 | } | 216 | } |
223 | 217 | ||
224 | return next() | 218 | return next() |
@@ -273,18 +267,18 @@ const usersUpdateMeValidator = [ | |||
273 | 267 | ||
274 | if (req.body.password || req.body.email) { | 268 | if (req.body.password || req.body.email) { |
275 | if (user.pluginAuth !== null) { | 269 | if (user.pluginAuth !== null) { |
276 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 270 | return res.fail({ message: 'You cannot update your email or password that is associated with an external auth system.' }) |
277 | .json({ error: 'You cannot update your email or password that is associated with an external auth system.' }) | ||
278 | } | 271 | } |
279 | 272 | ||
280 | if (!req.body.currentPassword) { | 273 | if (!req.body.currentPassword) { |
281 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 274 | return res.fail({ message: 'currentPassword parameter is missing.' }) |
282 | .json({ error: 'currentPassword parameter is missing.' }) | ||
283 | } | 275 | } |
284 | 276 | ||
285 | if (await user.isPasswordMatch(req.body.currentPassword) !== true) { | 277 | if (await user.isPasswordMatch(req.body.currentPassword) !== true) { |
286 | return res.status(HttpStatusCode.UNAUTHORIZED_401) | 278 | return res.fail({ |
287 | .json({ error: 'currentPassword is invalid.' }) | 279 | status: HttpStatusCode.UNAUTHORIZED_401, |
280 | message: 'currentPassword is invalid.' | ||
281 | }) | ||
288 | } | 282 | } |
289 | } | 283 | } |
290 | 284 | ||
@@ -335,8 +329,10 @@ const ensureUserRegistrationAllowed = [ | |||
335 | ) | 329 | ) |
336 | 330 | ||
337 | if (allowedResult.allowed === false) { | 331 | if (allowedResult.allowed === false) { |
338 | return res.status(HttpStatusCode.FORBIDDEN_403) | 332 | return res.fail({ |
339 | .json({ error: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.' }) | 333 | status: HttpStatusCode.FORBIDDEN_403, |
334 | message: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.' | ||
335 | }) | ||
340 | } | 336 | } |
341 | 337 | ||
342 | return next() | 338 | return next() |
@@ -348,8 +344,10 @@ const ensureUserRegistrationAllowedForIP = [ | |||
348 | const allowed = isSignupAllowedForCurrentIP(req.ip) | 344 | const allowed = isSignupAllowedForCurrentIP(req.ip) |
349 | 345 | ||
350 | if (allowed === false) { | 346 | if (allowed === false) { |
351 | return res.status(HttpStatusCode.FORBIDDEN_403) | 347 | return res.fail({ |
352 | .json({ error: 'You are not on a network authorized for registration.' }) | 348 | status: HttpStatusCode.FORBIDDEN_403, |
349 | message: 'You are not on a network authorized for registration.' | ||
350 | }) | ||
353 | } | 351 | } |
354 | 352 | ||
355 | return next() | 353 | return next() |
@@ -390,9 +388,10 @@ const usersResetPasswordValidator = [ | |||
390 | const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id) | 388 | const redisVerificationString = await Redis.Instance.getResetPasswordLink(user.id) |
391 | 389 | ||
392 | if (redisVerificationString !== req.body.verificationString) { | 390 | if (redisVerificationString !== req.body.verificationString) { |
393 | return res | 391 | return res.fail({ |
394 | .status(HttpStatusCode.FORBIDDEN_403) | 392 | status: HttpStatusCode.FORBIDDEN_403, |
395 | .json({ error: 'Invalid verification string.' }) | 393 | message: 'Invalid verification string.' |
394 | }) | ||
396 | } | 395 | } |
397 | 396 | ||
398 | return next() | 397 | return next() |
@@ -437,9 +436,10 @@ const usersVerifyEmailValidator = [ | |||
437 | const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id) | 436 | const redisVerificationString = await Redis.Instance.getVerifyEmailLink(user.id) |
438 | 437 | ||
439 | if (redisVerificationString !== req.body.verificationString) { | 438 | if (redisVerificationString !== req.body.verificationString) { |
440 | return res | 439 | return res.fail({ |
441 | .status(HttpStatusCode.FORBIDDEN_403) | 440 | status: HttpStatusCode.FORBIDDEN_403, |
442 | .json({ error: 'Invalid verification string.' }) | 441 | message: 'Invalid verification string.' |
442 | }) | ||
443 | } | 443 | } |
444 | 444 | ||
445 | return next() | 445 | return next() |
@@ -455,8 +455,10 @@ const ensureAuthUserOwnsAccountValidator = [ | |||
455 | const user = res.locals.oauth.token.User | 455 | const user = res.locals.oauth.token.User |
456 | 456 | ||
457 | if (res.locals.account.id !== user.Account.id) { | 457 | if (res.locals.account.id !== user.Account.id) { |
458 | return res.status(HttpStatusCode.FORBIDDEN_403) | 458 | return res.fail({ |
459 | .json({ error: 'Only owner can access ratings list.' }) | 459 | status: HttpStatusCode.FORBIDDEN_403, |
460 | message: 'Only owner can access ratings list.' | ||
461 | }) | ||
460 | } | 462 | } |
461 | 463 | ||
462 | return next() | 464 | return next() |
@@ -471,8 +473,10 @@ const ensureCanManageUser = [ | |||
471 | if (authUser.role === UserRole.ADMINISTRATOR) return next() | 473 | if (authUser.role === UserRole.ADMINISTRATOR) return next() |
472 | if (authUser.role === UserRole.MODERATOR && onUser.role === UserRole.USER) return next() | 474 | if (authUser.role === UserRole.MODERATOR && onUser.role === UserRole.USER) return next() |
473 | 475 | ||
474 | return res.status(HttpStatusCode.FORBIDDEN_403) | 476 | return res.fail({ |
475 | .json({ error: 'A moderator can only manager users.' }) | 477 | status: HttpStatusCode.FORBIDDEN_403, |
478 | message: 'A moderator can only manager users.' | ||
479 | }) | ||
476 | } | 480 | } |
477 | ] | 481 | ] |
478 | 482 | ||
@@ -515,15 +519,19 @@ async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: | |||
515 | const user = await UserModel.loadByUsernameOrEmail(username, email) | 519 | const user = await UserModel.loadByUsernameOrEmail(username, email) |
516 | 520 | ||
517 | if (user) { | 521 | if (user) { |
518 | res.status(HttpStatusCode.CONFLICT_409) | 522 | res.fail({ |
519 | .json({ error: 'User with this username or email already exists.' }) | 523 | status: HttpStatusCode.CONFLICT_409, |
524 | message: 'User with this username or email already exists.' | ||
525 | }) | ||
520 | return false | 526 | return false |
521 | } | 527 | } |
522 | 528 | ||
523 | const actor = await ActorModel.loadLocalByName(username) | 529 | const actor = await ActorModel.loadLocalByName(username) |
524 | if (actor) { | 530 | if (actor) { |
525 | res.status(HttpStatusCode.CONFLICT_409) | 531 | res.fail({ |
526 | .json({ error: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' }) | 532 | status: HttpStatusCode.CONFLICT_409, |
533 | message: 'Another actor (account/channel) with this name on this instance already exists or has already existed.' | ||
534 | }) | ||
527 | return false | 535 | return false |
528 | } | 536 | } |
529 | 537 | ||
@@ -535,14 +543,15 @@ async function checkUserExist (finder: () => Promise<MUserDefault>, res: express | |||
535 | 543 | ||
536 | if (!user) { | 544 | if (!user) { |
537 | if (abortResponse === true) { | 545 | if (abortResponse === true) { |
538 | res.status(HttpStatusCode.NOT_FOUND_404) | 546 | res.fail({ |
539 | .json({ error: 'User not found' }) | 547 | status: HttpStatusCode.NOT_FOUND_404, |
548 | message: 'User not found' | ||
549 | }) | ||
540 | } | 550 | } |
541 | 551 | ||
542 | return false | 552 | return false |
543 | } | 553 | } |
544 | 554 | ||
545 | res.locals.user = user | 555 | res.locals.user = user |
546 | |||
547 | return true | 556 | return true |
548 | } | 557 | } |
diff --git a/server/middlewares/validators/utils.ts b/server/middlewares/validators/utils.ts index 4167f6d43..e291f1b17 100644 --- a/server/middlewares/validators/utils.ts +++ b/server/middlewares/validators/utils.ts | |||
@@ -1,15 +1,19 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { query, validationResult } from 'express-validator' | 2 | import { query, validationResult } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
5 | 4 | ||
6 | function areValidationErrors (req: express.Request, res: express.Response) { | 5 | function areValidationErrors (req: express.Request, res: express.Response) { |
7 | const errors = validationResult(req) | 6 | const errors = validationResult(req) |
8 | 7 | ||
9 | if (!errors.isEmpty()) { | 8 | if (!errors.isEmpty()) { |
10 | logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) | 9 | logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) |
11 | res.status(HttpStatusCode.BAD_REQUEST_400) | 10 | res.fail({ |
12 | .json({ errors: errors.mapped() }) | 11 | message: 'Incorrect request parameters: ' + Object.keys(errors.mapped()).join(', '), |
12 | instance: req.originalUrl, | ||
13 | data: { | ||
14 | 'invalid-params': errors.mapped() | ||
15 | } | ||
16 | }) | ||
13 | 17 | ||
14 | return true | 18 | return true |
15 | } | 19 | } |
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 | ||
155 | function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { | 158 | function 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 | ||
156 | function isVideoCommentsEnabled (video: MVideo, res: express.Response) { | 156 | function 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 | ||
167 | function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MCommentOwnerVideoReply, res: express.Response) { | 168 | function 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 | ||
402 | function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { | 403 | function 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 | */ |
106 | const videosAddResumableValidator = [ | 109 | const 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 | ||
253 | const videosCustomGetValidator = ( | 263 | const 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 | ||
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index c2dfccc96..097a5ece1 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts | |||
@@ -21,9 +21,10 @@ const webfingerValidator = [ | |||
21 | 21 | ||
22 | const actor = await ActorModel.loadLocalUrlByName(name) | 22 | const actor = await ActorModel.loadLocalUrlByName(name) |
23 | if (!actor) { | 23 | if (!actor) { |
24 | return res.status(HttpStatusCode.NOT_FOUND_404) | 24 | return res.fail({ |
25 | .send({ error: 'Actor not found' }) | 25 | status: HttpStatusCode.NOT_FOUND_404, |
26 | .end() | 26 | message: 'Actor not found' |
27 | }) | ||
27 | } | 28 | } |
28 | 29 | ||
29 | res.locals.actorUrl = actor | 30 | res.locals.actorUrl = actor |
diff --git a/server/tests/api/users/users-verification.ts b/server/tests/api/users/users-verification.ts index 1a9a519a0..e0f2f2112 100644 --- a/server/tests/api/users/users-verification.ts +++ b/server/tests/api/users/users-verification.ts | |||
@@ -19,6 +19,7 @@ import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/l | |||
19 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' | 19 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' |
20 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 20 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
21 | import { User } from '../../../../shared/models/users' | 21 | import { User } from '../../../../shared/models/users' |
22 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
22 | 23 | ||
23 | const expect = chai.expect | 24 | const expect = chai.expect |
24 | 25 | ||
@@ -89,8 +90,8 @@ describe('Test users account verification', function () { | |||
89 | }) | 90 | }) |
90 | 91 | ||
91 | it('Should not allow login for user with unverified email', async function () { | 92 | it('Should not allow login for user with unverified email', async function () { |
92 | const resLogin = await login(server.url, server.client, user1, 400) | 93 | const resLogin = await login(server.url, server.client, user1, HttpStatusCode.BAD_REQUEST_400) |
93 | expect(resLogin.body.error).to.contain('User email is not verified.') | 94 | expect(resLogin.body.detail).to.contain('User email is not verified.') |
94 | }) | 95 | }) |
95 | 96 | ||
96 | it('Should verify the user via email and allow login', async function () { | 97 | it('Should verify the user via email and allow login', async function () { |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index cea98aac7..363236b62 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -93,16 +93,16 @@ describe('Test users', function () { | |||
93 | const client = { id: 'client', secret: server.client.secret } | 93 | const client = { id: 'client', secret: server.client.secret } |
94 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) | 94 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) |
95 | 95 | ||
96 | expect(res.body.code).to.equal('invalid_client') | 96 | expect(res.body.type).to.equal('invalid_client') |
97 | expect(res.body.error).to.contain('client is invalid') | 97 | expect(res.body.detail).to.contain('client is invalid') |
98 | }) | 98 | }) |
99 | 99 | ||
100 | it('Should not login with an invalid client secret', async function () { | 100 | it('Should not login with an invalid client secret', async function () { |
101 | const client = { id: server.client.id, secret: 'coucou' } | 101 | const client = { id: server.client.id, secret: 'coucou' } |
102 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) | 102 | const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) |
103 | 103 | ||
104 | expect(res.body.code).to.equal('invalid_client') | 104 | expect(res.body.type).to.equal('invalid_client') |
105 | expect(res.body.error).to.contain('client is invalid') | 105 | expect(res.body.detail).to.contain('client is invalid') |
106 | }) | 106 | }) |
107 | }) | 107 | }) |
108 | 108 | ||
@@ -112,16 +112,16 @@ describe('Test users', function () { | |||
112 | const user = { username: 'captain crochet', password: server.user.password } | 112 | const user = { username: 'captain crochet', password: server.user.password } |
113 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) | 113 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) |
114 | 114 | ||
115 | expect(res.body.code).to.equal('invalid_grant') | 115 | expect(res.body.type).to.equal('invalid_grant') |
116 | expect(res.body.error).to.contain('credentials are invalid') | 116 | expect(res.body.detail).to.contain('credentials are invalid') |
117 | }) | 117 | }) |
118 | 118 | ||
119 | it('Should not login with an invalid password', async function () { | 119 | it('Should not login with an invalid password', async function () { |
120 | const user = { username: server.user.username, password: 'mew_three' } | 120 | const user = { username: server.user.username, password: 'mew_three' } |
121 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) | 121 | const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) |
122 | 122 | ||
123 | expect(res.body.code).to.equal('invalid_grant') | 123 | expect(res.body.type).to.equal('invalid_grant') |
124 | expect(res.body.error).to.contain('credentials are invalid') | 124 | expect(res.body.detail).to.contain('credentials are invalid') |
125 | }) | 125 | }) |
126 | 126 | ||
127 | it('Should not be able to upload a video', async function () { | 127 | it('Should not be able to upload a video', async function () { |
diff --git a/server/typings/express/index.d.ts b/server/typings/express/index.d.ts index 55b6e0039..f58436ce1 100644 --- a/server/typings/express/index.d.ts +++ b/server/typings/express/index.d.ts | |||
@@ -22,6 +22,7 @@ import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video- | |||
22 | import { HttpMethod } from '@shared/core-utils/miscs/http-methods' | 22 | import { HttpMethod } from '@shared/core-utils/miscs/http-methods' |
23 | import { VideoCreate } from '@shared/models' | 23 | import { VideoCreate } from '@shared/models' |
24 | import { File as UploadXFile, Metadata } from '@uploadx/core' | 24 | import { File as UploadXFile, Metadata } from '@uploadx/core' |
25 | import { ProblemDocumentOptions } from 'http-problem-details/dist/ProblemDocument' | ||
25 | import { RegisteredPlugin } from '../../lib/plugins/plugin-manager' | 26 | import { RegisteredPlugin } from '../../lib/plugins/plugin-manager' |
26 | import { | 27 | import { |
27 | MAccountDefault, | 28 | MAccountDefault, |
@@ -83,8 +84,15 @@ declare module 'express' { | |||
83 | filename: string | 84 | filename: string |
84 | } | 85 | } |
85 | 86 | ||
86 | // Extends locals property from Response | 87 | // Extends Response with added functions and potential variables passed by middlewares |
87 | interface Response { | 88 | interface Response { |
89 | docs?: string | ||
90 | fail: (options: { | ||
91 | data?: Record<string, Object> | ||
92 | docs?: string | ||
93 | message: string | ||
94 | } & ProblemDocumentOptions) => void | ||
95 | |||
88 | locals: { | 96 | locals: { |
89 | videoAll?: MVideoFullLight | 97 | videoAll?: MVideoFullLight |
90 | onlyImmutableVideo?: MVideoImmutable | 98 | onlyImmutableVideo?: MVideoImmutable |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 34bf9c411..52a834056 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -38,46 +38,53 @@ info: | |||
38 | # Errors | 38 | # Errors |
39 | 39 | ||
40 | The API uses standard HTTP status codes to indicate the success or failure | 40 | The API uses standard HTTP status codes to indicate the success or failure |
41 | of the API call. | 41 | of the API call, completed by a [RFC7807-compliant](https://tools.ietf.org/html/rfc7807) response body. |
42 | 42 | ||
43 | ``` | 43 | ``` |
44 | HTTP 1.1 404 Not Found | 44 | HTTP 1.1 404 Not Found |
45 | Content-Type: application/json | 45 | Content-Type: application/problem+json; charset=utf-8 |
46 | 46 | ||
47 | { | 47 | { |
48 | "errorCode": 1 | 48 | "detail": "Video not found", |
49 | "error": "Account not found" | 49 | "status": 404, |
50 | "title": "Not Found", | ||
51 | "type": "about:blank" | ||
50 | } | 52 | } |
51 | ``` | 53 | ``` |
52 | 54 | ||
53 | We provide error codes for [a growing number of cases](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/server/server-error-code.enum.ts), | 55 | We provide error types for [a growing number of cases](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/server/server-error-code.enum.ts), |
54 | but it is still optional. | 56 | but it is still optional. |
55 | 57 | ||
56 | ### Validation errors | 58 | ### Validation errors |
57 | 59 | ||
58 | Each parameter is evaluated on its own against a set of rules before the route validator | 60 | Each parameter is evaluated on its own against a set of rules before the route validator |
59 | proceeds with potential testing involving parameter combinations. Errors coming from Validation | 61 | proceeds with potential testing involving parameter combinations. Errors coming from validation |
60 | errors appear earlier and benefit from a more detailed error type: | 62 | errors appear earlier and benefit from a more detailed error type: |
61 | 63 | ||
62 | ``` | 64 | ``` |
63 | HTTP 1.1 400 Bad Request | 65 | HTTP 1.1 400 Bad Request |
64 | Content-Type: application/json | 66 | Content-Type: application/problem+json; charset=utf-8 |
65 | 67 | ||
66 | { | 68 | { |
67 | "errors": { | 69 | "detail": "Incorrect request parameters: id", |
70 | "instance": "/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180", | ||
71 | "invalid-params": { | ||
68 | "id": { | 72 | "id": { |
69 | "value": "a117eb-c6a9-4756-bb09-2a956239f", | 73 | "location": "params", |
70 | "msg": "Should have a valid id", | 74 | "msg": "Invalid value", |
71 | "param": "id", | 75 | "param": "id", |
72 | "location": "params" | 76 | "value": "9c9de5e8-0a1e-484a-b099-e80766180" |
73 | } | 77 | } |
74 | } | 78 | }, |
79 | "status": 400, | ||
80 | "title": "Bad Request", | ||
81 | "type": "about:blank" | ||
75 | } | 82 | } |
76 | ``` | 83 | ``` |
77 | 84 | ||
78 | Where `id` is the name of the field concerned by the error, within the route definition. | 85 | Where `id` is the name of the field concerned by the error, within the route definition. |
79 | `errors.<field>.location` can be either 'params', 'body', 'header', 'query' or 'cookies', and | 86 | `invalid-params.<field>.location` can be either 'params', 'body', 'header', 'query' or 'cookies', and |
80 | `errors.<field>.value` reports the value that didn't pass validation whose `errors.<field>.msg` | 87 | `invalid-params.<field>.value` reports the value that didn't pass validation whose `invalid-params.<field>.msg` |
81 | is about. | 88 | is about. |
82 | 89 | ||
83 | # Rate limits | 90 | # Rate limits |
@@ -4136,6 +4136,11 @@ http-parser-js@^0.5.2: | |||
4136 | resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" | 4136 | resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" |
4137 | integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== | 4137 | integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== |
4138 | 4138 | ||
4139 | http-problem-details@^0.1.5: | ||
4140 | version "0.1.5" | ||
4141 | resolved "https://registry.yarnpkg.com/http-problem-details/-/http-problem-details-0.1.5.tgz#f8f94f4ab9d4050749e9f8566fb85bb8caa2be56" | ||
4142 | integrity sha512-GHxfQZ0POP4FWbAM0guOyZyJNWwbLUXp+4XOJdmitS2tp3gHVSatrSX59Yyq/dCkhk4KiGtTWIlXZC83yCkBkA== | ||
4143 | |||
4139 | http-signature@1.3.5: | 4144 | http-signature@1.3.5: |
4140 | version "1.3.5" | 4145 | version "1.3.5" |
4141 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.5.tgz#9f19496ffbf3227298d7b5f156e0e1a948678683" | 4146 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.5.tgz#9f19496ffbf3227298d7b5f156e0e1a948678683" |