aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-12-08 21:16:10 +0100
committerGitHub <noreply@github.com>2020-12-08 21:16:10 +0100
commitf2eb23cd87cf32b8fe545178143b5f49e06a58da (patch)
treeaf7d59945af70e28fd85047e2c688c59a908f548
parentc977fd3ec931c059111ddb2b8d6ddbb20b6b99a1 (diff)
downloadPeerTube-f2eb23cd87cf32b8fe545178143b5f49e06a58da.tar.gz
PeerTube-f2eb23cd87cf32b8fe545178143b5f49e06a58da.tar.zst
PeerTube-f2eb23cd87cf32b8fe545178143b5f49e06a58da.zip
emit more specific status codes on video upload (#3423)
- reduce http status codes list to potentially useful codes - convert more codes to typed ones - factorize html generator for error responses
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts3
-rw-r--r--client/src/app/+accounts/accounts.component.ts6
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts3
-rw-r--r--client/src/app/+video-channels/video-channels.component.ts6
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts22
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts24
-rw-r--r--client/src/app/core/auth/auth.service.ts3
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts9
-rw-r--r--client/src/standalone/videos/embed.ts13
-rw-r--r--server/controllers/client.ts32
-rw-r--r--server/controllers/static.ts14
-rw-r--r--server/helpers/custom-validators/misc.ts46
-rw-r--r--server/helpers/custom-validators/videos.ts8
-rw-r--r--server/helpers/youtube-dl.ts5
-rw-r--r--server/lib/activitypub/actor.ts3
-rw-r--r--server/lib/activitypub/playlist.ts3
-rw-r--r--server/lib/activitypub/videos.ts3
-rw-r--r--server/lib/client-html.ts42
-rw-r--r--server/middlewares/cache.ts6
-rw-r--r--server/middlewares/validators/videos/videos.ts41
-rw-r--r--server/tests/api/activitypub/client.ts3
-rw-r--r--server/tests/api/activitypub/refresher.ts15
-rw-r--r--server/tests/api/check-params/accounts.ts3
-rw-r--r--server/tests/api/check-params/contact-form.ts59
-rw-r--r--server/tests/api/check-params/users.ts12
-rw-r--r--server/tests/api/check-params/videos.ts18
-rw-r--r--server/tests/api/live/live-save-replay.ts33
-rw-r--r--server/tests/api/live/live.ts19
-rw-r--r--server/tests/api/server/config.ts9
-rw-r--r--server/tests/api/server/contact-form.ts3
-rw-r--r--server/tests/api/server/email.ts13
-rw-r--r--server/tests/api/server/follow-constraints.ts17
-rw-r--r--server/tests/api/server/handle-down.ts3
-rw-r--r--server/tests/api/server/no-client.ts3
-rw-r--r--server/tests/api/server/reverse-proxy.ts25
-rw-r--r--server/tests/api/videos/multiple-servers.ts5
-rw-r--r--server/tests/api/videos/video-change-ownership.ts14
-rw-r--r--server/tests/api/videos/video-hls.ts9
-rw-r--r--server/tests/api/videos/video-playlists.ts13
-rw-r--r--server/tests/api/videos/video-privacy.ts13
-rw-r--r--server/tests/api/videos/videos-history.ts3
-rw-r--r--server/tests/cli/reset-password.ts3
-rw-r--r--server/tests/feeds/feeds.ts17
-rw-r--r--server/tests/plugins/filter-hooks.ts24
-rw-r--r--server/tests/plugins/video-constants.ts10
-rw-r--r--server/tools/peertube-redundancy.ts5
-rw-r--r--shared/core-utils/miscs/http-error-codes.ts111
-rw-r--r--shared/extra-utils/server/activitypub.ts3
-rw-r--r--shared/extra-utils/server/redundancy.ts8
-rw-r--r--shared/extra-utils/users/login.ts13
-rw-r--r--shared/extra-utils/videos/video-history.ts8
-rw-r--r--shared/extra-utils/videos/videos.ts4
-rw-r--r--support/doc/api/openapi.yaml20
53 files changed, 503 insertions, 307 deletions
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index 11e442f6b..ac2a6c980 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -10,6 +10,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
10import { InstanceService } from '@app/shared/shared-instance' 10import { InstanceService } from '@app/shared/shared-instance'
11import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 11import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
12import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 12import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
13import { ServerConfig } from '@shared/models' 14import { ServerConfig } from '@shared/models'
14 15
15@Component({ 16@Component({
@@ -78,7 +79,7 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
78 }, 79 },
79 80
80 err => { 81 err => {
81 this.error = err.status === 403 82 this.error = err.status === HttpStatusCode.FORBIDDEN_403
82 ? $localize`You already sent this form recently` 83 ? $localize`You already sent this form recently`
83 : err.message 84 : err.message
84 } 85 }
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index dbc7c8887..4820eaf32 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -6,6 +6,7 @@ import { AuthService, Notifier, RedirectService, RestExtractor, ScreenService, U
6import { Account, AccountService, DropdownAction, ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' 6import { Account, AccountService, DropdownAction, ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { AccountReportComponent } from '@app/shared/shared-moderation' 7import { AccountReportComponent } from '@app/shared/shared-moderation'
8import { User, UserRight } from '@shared/models' 8import { User, UserRight } from '@shared/models'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
9 10
10@Component({ 11@Component({
11 templateUrl: './accounts.component.html', 12 templateUrl: './accounts.component.html',
@@ -47,7 +48,10 @@ export class AccountsComponent implements OnInit, OnDestroy {
47 switchMap(accountId => this.accountService.getAccount(accountId)), 48 switchMap(accountId => this.accountService.getAccount(accountId)),
48 tap(account => this.onAccount(account)), 49 tap(account => this.onAccount(account)),
49 switchMap(account => this.videoChannelService.listAccountVideoChannels(account)), 50 switchMap(account => this.videoChannelService.listAccountVideoChannels(account)),
50 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ])) 51 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
52 HttpStatusCode.BAD_REQUEST_400,
53 HttpStatusCode.NOT_FOUND_404
54 ]))
51 ) 55 )
52 .subscribe( 56 .subscribe(
53 videoChannels => this.videoChannels = videoChannels.data, 57 videoChannels => this.videoChannels = videoChannels.data,
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
index 1d0cbf246..a625493de 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
@@ -10,6 +10,7 @@ import {
10import { FormValidatorService } from '@app/shared/shared-forms' 10import { FormValidatorService } from '@app/shared/shared-forms'
11import { VideoChannelService } from '@app/shared/shared-main' 11import { VideoChannelService } from '@app/shared/shared-main'
12import { VideoChannelCreate } from '@shared/models' 12import { VideoChannelCreate } from '@shared/models'
13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
13import { MyVideoChannelEdit } from './my-video-channel-edit' 14import { MyVideoChannelEdit } from './my-video-channel-edit'
14 15
15@Component({ 16@Component({
@@ -58,7 +59,7 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
58 }, 59 },
59 60
60 err => { 61 err => {
61 if (err.status === 409) { 62 if (err.status === HttpStatusCode.CONFLICT_409) {
62 this.error = $localize`This name already exists on this instance.` 63 this.error = $localize`This name already exists on this instance.`
63 return 64 return
64 } 65 }
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index ea8bda1cf..d2fd265c4 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -6,6 +6,7 @@ import { ActivatedRoute } from '@angular/router'
6import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core' 6import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core'
7import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' 7import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
8import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 8import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
9 10
10@Component({ 11@Component({
11 templateUrl: './video-channels.component.html', 12 templateUrl: './video-channels.component.html',
@@ -37,7 +38,10 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
37 map(params => params[ 'videoChannelName' ]), 38 map(params => params[ 'videoChannelName' ]),
38 distinctUntilChanged(), 39 distinctUntilChanged(),
39 switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)), 40 switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
40 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ])) 41 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
42 HttpStatusCode.BAD_REQUEST_400,
43 HttpStatusCode.NOT_FOUND_404
44 ]))
41 ) 45 )
42 .subscribe(videoChannel => { 46 .subscribe(videoChannel => {
43 this.videoChannel = videoChannel 47 this.videoChannel = videoChannel
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index bee3679f7..cafb030b9 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -9,6 +9,7 @@ import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/sh
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { VideoPrivacy } from '@shared/models' 10import { VideoPrivacy } from '@shared/models'
11import { VideoSend } from './video-send' 11import { VideoSend } from './video-send'
12import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
12 13
13@Component({ 14@Component({
14 selector: 'my-video-upload', 15 selector: 'my-video-upload',
@@ -129,17 +130,17 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
129 cancelUpload () { 130 cancelUpload () {
130 if (this.videoUploadObservable !== null) { 131 if (this.videoUploadObservable !== null) {
131 this.videoUploadObservable.unsubscribe() 132 this.videoUploadObservable.unsubscribe()
133 }
132 134
133 this.isUploadingVideo = false 135 this.isUploadingVideo = false
134 this.videoUploadPercents = 0 136 this.videoUploadPercents = 0
135 this.videoUploadObservable = null 137 this.videoUploadObservable = null
136 138
137 this.firstStepError.emit() 139 this.firstStepError.emit()
138 this.enableRetryAfterError = false 140 this.enableRetryAfterError = false
139 this.error = '' 141 this.error = ''
140 142
141 this.notifier.info($localize`Upload cancelled`) 143 this.notifier.info($localize`Upload cancelled`)
142 }
143 } 144 }
144 145
145 uploadFirstStep (clickedOnButton = false) { 146 uploadFirstStep (clickedOnButton = false) {
@@ -229,6 +230,11 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
229 notifier: this.notifier, 230 notifier: this.notifier,
230 sticky: false 231 sticky: false
231 }) 232 })
233
234 if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413 ||
235 err.status === HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) {
236 this.cancelUpload()
237 }
232 } 238 }
233 ) 239 )
234 } 240 }
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 b15de2a79..33de901c0 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -39,6 +39,7 @@ import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils'
39import { environment } from '../../../environments/environment' 39import { environment } from '../../../environments/environment'
40import { VideoSupportComponent } from './modal/video-support.component' 40import { VideoSupportComponent } from './modal/video-support.component'
41import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 41import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
42import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
42 43
43type URLOptions = CustomizationOptions & { playerMode: PlayerMode } 44type URLOptions = CustomizationOptions & { playerMode: PlayerMode }
44 45
@@ -412,13 +413,25 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
412 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`, 413 $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`,
413 $localize`Redirection` 414 $localize`Redirection`
414 ).then(res => { 415 ).then(res => {
415 if (res === false) return this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]) 416 if (res === false) {
417 return this.restExtractor.redirectTo404IfNotFound(err, [
418 HttpStatusCode.BAD_REQUEST_400,
419 HttpStatusCode.UNAUTHORIZED_401,
420 HttpStatusCode.FORBIDDEN_403,
421 HttpStatusCode.NOT_FOUND_404
422 ])
423 }
416 424
417 return window.location.href = originUrl 425 return window.location.href = originUrl
418 }) 426 })
419 } 427 }
420 428
421 return this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]) 429 return this.restExtractor.redirectTo404IfNotFound(err, [
430 HttpStatusCode.BAD_REQUEST_400,
431 HttpStatusCode.UNAUTHORIZED_401,
432 HttpStatusCode.FORBIDDEN_403,
433 HttpStatusCode.NOT_FOUND_404
434 ])
422 }) 435 })
423 ) 436 )
424 .subscribe(([ video, captionsResult ]) => { 437 .subscribe(([ video, captionsResult ]) => {
@@ -450,7 +463,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
450 this.playlistService.getVideoPlaylist(playlistId) 463 this.playlistService.getVideoPlaylist(playlistId)
451 .pipe( 464 .pipe(
452 // If 401, the video is private or blocked so redirect to 404 465 // If 401, the video is private or blocked so redirect to 404
453 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ])) 466 catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
467 HttpStatusCode.BAD_REQUEST_400,
468 HttpStatusCode.UNAUTHORIZED_401,
469 HttpStatusCode.FORBIDDEN_403,
470 HttpStatusCode.NOT_FOUND_404
471 ]))
454 ) 472 )
455 .subscribe(playlist => { 473 .subscribe(playlist => {
456 this.playlist = playlist 474 this.playlist = playlist
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index fd6062d3f..cdf13186b 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -11,6 +11,7 @@ import { environment } from '../../../environments/environment'
11import { RestExtractor } from '../rest/rest-extractor.service' 11import { RestExtractor } from '../rest/rest-extractor.service'
12import { AuthStatus } from './auth-status.model' 12import { AuthStatus } from './auth-status.model'
13import { AuthUser } from './auth-user.model' 13import { AuthUser } from './auth-user.model'
14import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
14 15
15interface UserLoginWithUsername extends UserLogin { 16interface UserLoginWithUsername extends UserLogin {
16 access_token: string 17 access_token: string
@@ -94,7 +95,7 @@ export class AuthService {
94 error => { 95 error => {
95 let errorMessage = error.message 96 let errorMessage = error.message
96 97
97 if (error.status === 403) { 98 if (error.status === HttpStatusCode.FORBIDDEN_403) {
98 errorMessage = $localize`Cannot retrieve OAuth Client credentials: ${error.text}. 99 errorMessage = $localize`Cannot retrieve OAuth Client credentials: ${error.text}.
99Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.` 100Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.`
100 } 101 }
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 4b8c1e155..84d9ed074 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { dateToHuman } from '@app/helpers' 4import { dateToHuman } from '@app/helpers'
5import { ResultList } from '@shared/models' 5import { ResultList } from '@shared/models'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
6 7
7@Injectable() 8@Injectable()
8export class RestExtractor { 9export class RestExtractor {
@@ -57,9 +58,9 @@ export class RestExtractor {
57 errorMessage = errorsArray.join('. ') 58 errorMessage = errorsArray.join('. ')
58 } else if (err.error && err.error.error) { 59 } else if (err.error && err.error.error) {
59 errorMessage = err.error.error 60 errorMessage = err.error.error
60 } else if (err.status === 413) { 61 } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
61 errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.` 62 errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.`
62 } else if (err.status === 429) { 63 } else if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) {
63 const secondsLeft = err.headers.get('retry-after') 64 const secondsLeft = err.headers.get('retry-after')
64 if (secondsLeft) { 65 if (secondsLeft) {
65 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60) 66 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
@@ -67,7 +68,7 @@ export class RestExtractor {
67 } else { 68 } else {
68 errorMessage = $localize`Too many attempts, please try again later.` 69 errorMessage = $localize`Too many attempts, please try again later.`
69 } 70 }
70 } else if (err.status === 500) { 71 } else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
71 errorMessage = $localize`Server error. Please retry later.` 72 errorMessage = $localize`Server error. Please retry later.`
72 } 73 }
73 74
@@ -92,7 +93,7 @@ export class RestExtractor {
92 return observableThrowError(errorObj) 93 return observableThrowError(errorObj)
93 } 94 }
94 95
95 redirectTo404IfNotFound (obj: { status: number }, status = [ 404 ]) { 96 redirectTo404IfNotFound (obj: { status: number }, status = [ HttpStatusCode.NOT_FOUND_404 ]) {
96 if (obj && obj.status && status.indexOf(obj.status) !== -1) { 97 if (obj && obj.status && status.indexOf(obj.status) !== -1) {
97 // Do not use redirectService to avoid circular dependencies 98 // Do not use redirectService to avoid circular dependencies
98 this.router.navigate([ '/404' ], { skipLocationChange: true }) 99 this.router.navigate([ '/404' ], { skipLocationChange: true })
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index 48f7e7749..1709d44e7 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -13,6 +13,7 @@ import {
13 PluginType, 13 PluginType,
14 ClientHookName 14 ClientHookName
15} from '../../../../shared/models' 15} from '../../../../shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
16import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager' 17import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
17import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 18import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
18import { TranslationsManager } from '../../assets/player/translations-manager' 19import { TranslationsManager } from '../../assets/player/translations-manager'
@@ -85,7 +86,7 @@ export class PeerTubeEmbed {
85 refreshFetch (url: string, options?: RequestInit) { 86 refreshFetch (url: string, options?: RequestInit) {
86 return fetch(url, options) 87 return fetch(url, options)
87 .then((res: Response) => { 88 .then((res: Response) => {
88 if (res.status !== 401) return res 89 if (res.status !== HttpStatusCode.UNAUTHORIZED_401) return res
89 90
90 const refreshingTokenPromise = new Promise((resolve, reject) => { 91 const refreshingTokenPromise = new Promise((resolve, reject) => {
91 const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) 92 const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
@@ -107,7 +108,7 @@ export class PeerTubeEmbed {
107 method: 'POST', 108 method: 'POST',
108 body: objectToUrlEncoded(data) 109 body: objectToUrlEncoded(data)
109 }).then(res => { 110 }).then(res => {
110 if (res.status === 401) return undefined 111 if (res.status === HttpStatusCode.UNAUTHORIZED_401) return undefined
111 112
112 return res.json() 113 return res.json()
113 }).then((obj: UserRefreshToken & { code: 'invalid_grant'}) => { 114 }).then((obj: UserRefreshToken & { code: 'invalid_grant'}) => {
@@ -338,7 +339,7 @@ export class PeerTubeEmbed {
338 339
339 try { 340 try {
340 playlistResponse = await playlistPromise 341 playlistResponse = await playlistPromise
341 isResponseOk = playlistResponse.status === 200 342 isResponseOk = playlistResponse.status === HttpStatusCode.OK_200
342 } catch (err) { 343 } catch (err) {
343 console.error(err) 344 console.error(err)
344 isResponseOk = false 345 isResponseOk = false
@@ -347,7 +348,7 @@ export class PeerTubeEmbed {
347 if (!isResponseOk) { 348 if (!isResponseOk) {
348 const serverTranslations = await this.translationsPromise 349 const serverTranslations = await this.translationsPromise
349 350
350 if (playlistResponse?.status === 404) { 351 if (playlistResponse?.status === HttpStatusCode.NOT_FOUND_404) {
351 this.playlistNotFound(serverTranslations) 352 this.playlistNotFound(serverTranslations)
352 return undefined 353 return undefined
353 } 354 }
@@ -367,7 +368,7 @@ export class PeerTubeEmbed {
367 368
368 try { 369 try {
369 videoResponse = await videoPromise 370 videoResponse = await videoPromise
370 isResponseOk = videoResponse.status === 200 371 isResponseOk = videoResponse.status === HttpStatusCode.OK_200
371 } catch (err) { 372 } catch (err) {
372 console.error(err) 373 console.error(err)
373 374
@@ -377,7 +378,7 @@ export class PeerTubeEmbed {
377 if (!isResponseOk) { 378 if (!isResponseOk) {
378 const serverTranslations = await this.translationsPromise 379 const serverTranslations = await this.translationsPromise
379 380
380 if (videoResponse?.status === 404) { 381 if (videoResponse?.status === HttpStatusCode.NOT_FOUND_404) {
381 this.videoNotFound(serverTranslations) 382 this.videoNotFound(serverTranslations)
382 return undefined 383 return undefined
383 } 384 }
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index 49e6fd661..bd1f19f8c 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -3,12 +3,11 @@ import { constants, promises as fs } from 'fs'
3import { join } from 'path' 3import { join } from 'path'
4import { CONFIG } from '@server/initializers/config' 4import { CONFIG } from '@server/initializers/config'
5import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 5import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
6import { HttpStatusCode } from '@shared/core-utils'
6import { root } from '../helpers/core-utils' 7import { root } from '../helpers/core-utils'
7import { logger } from '../helpers/logger' 8import { STATIC_MAX_AGE } from '../initializers/constants'
8import { ACCEPT_HEADERS, STATIC_MAX_AGE } from '../initializers/constants' 9import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
9import { ClientHtml } from '../lib/client-html'
10import { asyncMiddleware, embedCSP } from '../middlewares' 10import { asyncMiddleware, embedCSP } from '../middlewares'
11import { HttpStatusCode } from '@shared/core-utils'
12 11
13const clientsRouter = express.Router() 12const clientsRouter = express.Router()
14 13
@@ -118,31 +117,12 @@ function serveServerTranslations (req: express.Request, res: express.Response) {
118 return res.sendStatus(HttpStatusCode.NOT_FOUND_404) 117 return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
119} 118}
120 119
121async function serveIndexHTML (req: express.Request, res: express.Response) {
122 if (req.accepts(ACCEPT_HEADERS) === 'html') {
123 try {
124 await generateHTMLPage(req, res, req.params.language)
125 return
126 } catch (err) {
127 logger.error('Cannot generate HTML page.', err)
128 }
129 }
130
131 return res.status(HttpStatusCode.INTERNAL_SERVER_ERROR_500).end()
132}
133
134async function generateEmbedHtmlPage (req: express.Request, res: express.Response) { 120async function generateEmbedHtmlPage (req: express.Request, res: express.Response) {
135 const html = await ClientHtml.getEmbedHTML() 121 const html = await ClientHtml.getEmbedHTML()
136 122
137 return sendHTML(html, res) 123 return sendHTML(html, res)
138} 124}
139 125
140async function generateHTMLPage (req: express.Request, res: express.Response, paramLang?: string) {
141 const html = await ClientHtml.getDefaultHTMLPage(req, res, paramLang)
142
143 return sendHTML(html, res)
144}
145
146async function generateWatchHtmlPage (req: express.Request, res: express.Response) { 126async function generateWatchHtmlPage (req: express.Request, res: express.Response) {
147 const html = await ClientHtml.getWatchHTMLPage(req.params.id + '', req, res) 127 const html = await ClientHtml.getWatchHTMLPage(req.params.id + '', req, res)
148 128
@@ -167,12 +147,6 @@ async function generateVideoChannelHtmlPage (req: express.Request, res: express.
167 return sendHTML(html, res) 147 return sendHTML(html, res)
168} 148}
169 149
170function sendHTML (html: string, res: express.Response) {
171 res.set('Content-Type', 'text/html; charset=UTF-8')
172
173 return res.send(html)
174}
175
176async function generateManifest (req: express.Request, res: express.Response) { 150async function generateManifest (req: express.Request, res: express.Response) {
177 const manifestPhysicalPath = join(root(), 'client', 'dist', 'manifest.webmanifest') 151 const manifestPhysicalPath = join(root(), 'client', 'dist', 'manifest.webmanifest')
178 const manifestJson = await fs.readFile(manifestPhysicalPath, 'utf8') 152 const manifestJson = await fs.readFile(manifestPhysicalPath, 'utf8')
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index ff77452dd..f12f00e1b 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -27,6 +27,7 @@ import { getTorrentFilePath, getVideoFilePath } from '@server/lib/video-paths'
27import { getThemeOrDefault } from '../lib/plugins/theme-utils' 27import { getThemeOrDefault } from '../lib/plugins/theme-utils'
28import { getEnabledResolutions, getRegisteredPlugins, getRegisteredThemes } from '@server/controllers/api/config' 28import { getEnabledResolutions, getRegisteredPlugins, getRegisteredThemes } from '@server/controllers/api/config'
29import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 29import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
30import { serveIndexHTML } from '@server/lib/client-html'
30 31
31const staticRouter = express.Router() 32const staticRouter = express.Router()
32 33
@@ -119,6 +120,11 @@ staticRouter.get('/robots.txt',
119 } 120 }
120) 121)
121 122
123staticRouter.all('/teapot',
124 getCup,
125 asyncMiddleware(serveIndexHTML)
126)
127
122// security.txt service 128// security.txt service
123staticRouter.get('/security.txt', 129staticRouter.get('/security.txt',
124 (_, res: express.Response) => { 130 (_, res: express.Response) => {
@@ -391,3 +397,11 @@ function getHLSPlaylist (video: MVideoFullLight) {
391 397
392 return Object.assign(playlist, { Video: video }) 398 return Object.assign(playlist, { Video: video })
393} 399}
400
401function getCup (req: express.Request, res: express.Response, next: express.NextFunction) {
402 res.status(HttpStatusCode.I_AM_A_TEAPOT_418)
403 res.setHeader('Accept-Additions', 'Non-Dairy;1,Sugar;1')
404 res.setHeader('Safe', 'if-sepia-awake')
405
406 return next()
407}
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 61c03f0c9..effdd98cb 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -86,6 +86,50 @@ function toIntArray (value: any) {
86 return value.map(v => validator.toInt(v)) 86 return value.map(v => validator.toInt(v))
87} 87}
88 88
89function isFileFieldValid (
90 files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
91 field: string,
92 optional = false
93) {
94 // Should have files
95 if (!files) return optional
96 if (isArray(files)) return optional
97
98 // Should have a file
99 const fileArray = files[field]
100 if (!fileArray || fileArray.length === 0) {
101 return optional
102 }
103
104 // The file should exist
105 const file = fileArray[0]
106 if (!file || !file.originalname) return false
107 return file
108}
109
110function isFileMimeTypeValid (
111 files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
112 mimeTypeRegex: string,
113 field: string,
114 optional = false
115) {
116 // Should have files
117 if (!files) return optional
118 if (isArray(files)) return optional
119
120 // Should have a file
121 const fileArray = files[field]
122 if (!fileArray || fileArray.length === 0) {
123 return optional
124 }
125
126 // The file should exist
127 const file = fileArray[0]
128 if (!file || !file.originalname) return false
129
130 return new RegExp(`^${mimeTypeRegex}$`, 'i').test(file.mimetype)
131}
132
89function isFileValid ( 133function isFileValid (
90 files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], 134 files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
91 mimeTypeRegex: string, 135 mimeTypeRegex: string,
@@ -132,5 +176,7 @@ export {
132 toIntOrNull, 176 toIntOrNull,
133 toArray, 177 toArray,
134 toIntArray, 178 toIntArray,
179 isFileFieldValid,
180 isFileMimeTypeValid,
135 isFileValid 181 isFileValid
136} 182}
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 8b309ae42..87966798f 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -11,7 +11,7 @@ import {
11 VIDEO_STATES, 11 VIDEO_STATES,
12 VIDEO_LIVE 12 VIDEO_LIVE
13} from '../../initializers/constants' 13} from '../../initializers/constants'
14import { exists, isArray, isDateValid, isFileValid } from './misc' 14import { exists, isArray, isDateValid, isFileMimeTypeValid, isFileValid } from './misc'
15import * as magnetUtil from 'magnet-uri' 15import * as magnetUtil from 'magnet-uri'
16 16
17const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS 17const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
@@ -81,8 +81,8 @@ function isVideoFileExtnameValid (value: string) {
81 return exists(value) && (value === VIDEO_LIVE.EXTENSION || MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined) 81 return exists(value) && (value === VIDEO_LIVE.EXTENSION || MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined)
82} 82}
83 83
84function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { 84function isVideoFileMimeTypeValid (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
85 return isFileValid(files, MIMETYPES.VIDEO.MIMETYPES_REGEX, 'videofile', null) 85 return isFileMimeTypeValid(files, MIMETYPES.VIDEO.MIMETYPES_REGEX, 'videofile')
86} 86}
87 87
88const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME 88const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
@@ -143,12 +143,12 @@ export {
143 isVideoFPSResolutionValid, 143 isVideoFPSResolutionValid,
144 isScheduleVideoUpdatePrivacyValid, 144 isScheduleVideoUpdatePrivacyValid,
145 isVideoOriginallyPublishedAtValid, 145 isVideoOriginallyPublishedAtValid,
146 isVideoFile,
147 isVideoMagnetUriValid, 146 isVideoMagnetUriValid,
148 isVideoStateValid, 147 isVideoStateValid,
149 isVideoViewsValid, 148 isVideoViewsValid,
150 isVideoRatingTypeValid, 149 isVideoRatingTypeValid,
151 isVideoFileExtnameValid, 150 isVideoFileExtnameValid,
151 isVideoFileMimeTypeValid,
152 isVideoDurationValid, 152 isVideoDurationValid,
153 isVideoTagValid, 153 isVideoTagValid,
154 isVideoPrivacyValid, 154 isVideoPrivacyValid,
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts
index 302b2e206..9e8ef90d8 100644
--- a/server/helpers/youtube-dl.ts
+++ b/server/helpers/youtube-dl.ts
@@ -7,6 +7,7 @@ import { ensureDir, remove, writeFile } from 'fs-extra'
7import * as request from 'request' 7import * as request from 'request'
8import { createWriteStream } from 'fs' 8import { createWriteStream } from 'fs'
9import { CONFIG } from '@server/initializers/config' 9import { CONFIG } from '@server/initializers/config'
10import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
10 11
11export type YoutubeDLInfo = { 12export type YoutubeDLInfo = {
12 name?: string 13 name?: string
@@ -154,7 +155,7 @@ async function updateYoutubeDLBinary () {
154 return res() 155 return res()
155 } 156 }
156 157
157 if (result.statusCode !== 302) { 158 if (result.statusCode !== HttpStatusCode.FOUND_302) {
158 logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) 159 logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode)
159 return res() 160 return res()
160 } 161 }
@@ -164,7 +165,7 @@ async function updateYoutubeDLBinary () {
164 const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(url)[1] 165 const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(url)[1]
165 166
166 downloadFile.on('response', result => { 167 downloadFile.on('response', result => {
167 if (result.statusCode !== 200) { 168 if (result.statusCode !== HttpStatusCode.OK_200) {
168 logger.error('Cannot update youtube-dl: new version response is not 200, it\'s %d.', result.statusCode) 169 logger.error('Cannot update youtube-dl: new version response is not 200, it\'s %d.', result.statusCode)
169 return res() 170 return res()
170 } 171 }
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index fb5558ff6..52547536c 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -36,6 +36,7 @@ import {
36} from '../../types/models' 36} from '../../types/models'
37import { extname } from 'path' 37import { extname } from 'path'
38import { getServerActor } from '@server/models/application/application' 38import { getServerActor } from '@server/models/application/application'
39import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
39 40
40// Set account keys, this could be long so process after the account creation and do not block the client 41// Set account keys, this could be long so process after the account creation and do not block the client
41function setAsyncActorKeys <T extends MActor> (actor: T) { 42function setAsyncActorKeys <T extends MActor> (actor: T) {
@@ -277,7 +278,7 @@ async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannel
277 278
278 const { result, statusCode } = await fetchRemoteActor(actorUrl) 279 const { result, statusCode } = await fetchRemoteActor(actorUrl)
279 280
280 if (statusCode === 404) { 281 if (statusCode === HttpStatusCode.NOT_FOUND_404) {
281 logger.info('Deleting actor %s because there is a 404 in refresh actor.', actor.url) 282 logger.info('Deleting actor %s because there is a 404 in refresh actor.', actor.url)
282 actor.Account 283 actor.Account
283 ? await actor.Account.destroy() 284 ? await actor.Account.destroy()
diff --git a/server/lib/activitypub/playlist.ts b/server/lib/activitypub/playlist.ts
index bd442b223..53298e968 100644
--- a/server/lib/activitypub/playlist.ts
+++ b/server/lib/activitypub/playlist.ts
@@ -18,6 +18,7 @@ import { createPlaylistMiniatureFromUrl } from '../thumbnail'
18import { FilteredModelAttributes } from '../../types/sequelize' 18import { FilteredModelAttributes } from '../../types/sequelize'
19import { MAccountDefault, MAccountId, MVideoId } from '../../types/models' 19import { MAccountDefault, MAccountId, MVideoId } from '../../types/models'
20import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../types/models/video/video-playlist' 20import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../types/models/video/video-playlist'
21import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
21 22
22function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { 23function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) {
23 const privacy = to.includes(ACTIVITY_PUB.PUBLIC) 24 const privacy = to.includes(ACTIVITY_PUB.PUBLIC)
@@ -120,7 +121,7 @@ async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner)
120 121
121 try { 122 try {
122 const { statusCode, playlistObject } = await fetchRemoteVideoPlaylist(videoPlaylist.url) 123 const { statusCode, playlistObject } = await fetchRemoteVideoPlaylist(videoPlaylist.url)
123 if (statusCode === 404) { 124 if (statusCode === HttpStatusCode.NOT_FOUND_404) {
124 logger.info('Cannot refresh remote video playlist %s: it does not exist anymore. Deleting it.', videoPlaylist.url) 125 logger.info('Cannot refresh remote video playlist %s: it does not exist anymore. Deleting it.', videoPlaylist.url)
125 126
126 await videoPlaylist.destroy() 127 await videoPlaylist.destroy()
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 04f0bfc23..b15d5da1c 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -76,6 +76,7 @@ import { sendCreateVideo, sendUpdateVideo } from './send'
76import { addVideoShares, shareVideoByServerAndChannel } from './share' 76import { addVideoShares, shareVideoByServerAndChannel } from './share'
77import { addVideoComments } from './video-comments' 77import { addVideoComments } from './video-comments'
78import { createRates } from './video-rates' 78import { createRates } from './video-rates'
79import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
79 80
80async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { 81async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) {
81 const video = videoArg as MVideoAP 82 const video = videoArg as MVideoAP
@@ -488,7 +489,7 @@ async function refreshVideoIfNeeded (options: {
488 489
489 try { 490 try {
490 const { response, videoObject } = await fetchRemoteVideo(video.url) 491 const { response, videoObject } = await fetchRemoteVideo(video.url)
491 if (response.statusCode === 404) { 492 if (response.statusCode === HttpStatusCode.NOT_FOUND_404) {
492 logger.info('Cannot refresh remote video %s: video does not exist anymore. Deleting it.', video.url) 493 logger.info('Cannot refresh remote video %s: video does not exist anymore. Deleting it.', video.url)
493 494
494 // Video does not exist anymore 495 // Video does not exist anymore
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index d97d23180..32f5d29ab 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -1,4 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as Bluebird from 'bluebird'
2import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' 3import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
3import { 4import {
4 AVATARS_SIZE, 5 AVATARS_SIZE,
@@ -6,7 +7,8 @@ import {
6 EMBED_SIZE, 7 EMBED_SIZE,
7 PLUGIN_GLOBAL_CSS_PATH, 8 PLUGIN_GLOBAL_CSS_PATH,
8 WEBSERVER, 9 WEBSERVER,
9 FILES_CONTENT_HASH 10 FILES_CONTENT_HASH,
11 ACCEPT_HEADERS
10} from '../initializers/constants' 12} from '../initializers/constants'
11import { join } from 'path' 13import { join } from 'path'
12import { escapeHTML, isTestInstance, sha256 } from '../helpers/core-utils' 14import { escapeHTML, isTestInstance, sha256 } from '../helpers/core-utils'
@@ -18,7 +20,6 @@ import { readFile } from 'fs-extra'
18import { getActivityStreamDuration } from '../models/video/video-format-utils' 20import { getActivityStreamDuration } from '../models/video/video-format-utils'
19import { AccountModel } from '../models/account/account' 21import { AccountModel } from '../models/account/account'
20import { VideoChannelModel } from '../models/video/video-channel' 22import { VideoChannelModel } from '../models/video/video-channel'
21import * as Bluebird from 'bluebird'
22import { CONFIG } from '../initializers/config' 23import { CONFIG } from '../initializers/config'
23import { logger } from '../helpers/logger' 24import { logger } from '../helpers/logger'
24import { MAccountActor, MChannelActor } from '../types/models' 25import { MAccountActor, MChannelActor } from '../types/models'
@@ -53,7 +54,7 @@ type Tags = {
53 } 54 }
54} 55}
55 56
56export class ClientHtml { 57class ClientHtml {
57 58
58 private static htmlCache: { [path: string]: string } = {} 59 private static htmlCache: { [path: string]: string } = {}
59 60
@@ -505,3 +506,38 @@ export class ClientHtml {
505 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, tagsString) 506 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, tagsString)
506 } 507 }
507} 508}
509
510function sendHTML (html: string, res: express.Response) {
511 res.set('Content-Type', 'text/html; charset=UTF-8')
512
513 return res.send(html)
514}
515
516async function serveIndexHTML (req: express.Request, res: express.Response) {
517 if (req.accepts(ACCEPT_HEADERS) === 'html' ||
518 !req.headers.accept) {
519 try {
520 await generateHTMLPage(req, res, req.params.language)
521 return
522 } catch (err) {
523 logger.error('Cannot generate HTML page.', err)
524 return res.sendStatus(HttpStatusCode.INTERNAL_SERVER_ERROR_500)
525 }
526 }
527
528 return res.sendStatus(HttpStatusCode.NOT_ACCEPTABLE_406)
529}
530
531// ---------------------------------------------------------------------------
532
533export {
534 ClientHtml,
535 sendHTML,
536 serveIndexHTML
537}
538
539async function generateHTMLPage (req: express.Request, res: express.Response, paramLang?: string) {
540 const html = await ClientHtml.getDefaultHTMLPage(req, res, paramLang)
541
542 return sendHTML(html, res)
543}
diff --git a/server/middlewares/cache.ts b/server/middlewares/cache.ts
index cb24d9e0e..0708ee8e8 100644
--- a/server/middlewares/cache.ts
+++ b/server/middlewares/cache.ts
@@ -1,5 +1,6 @@
1import { Redis } from '../lib/redis' 1import { Redis } from '../lib/redis'
2import * as apicache from 'apicache' 2import * as apicache from 'apicache'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
3 4
4// Ensure Redis is initialized 5// Ensure Redis is initialized
5Redis.Instance.init() 6Redis.Instance.init()
@@ -8,7 +9,10 @@ const defaultOptions = {
8 redisClient: Redis.Instance.getClient(), 9 redisClient: Redis.Instance.getClient(),
9 appendKey: () => Redis.Instance.getPrefix(), 10 appendKey: () => Redis.Instance.getPrefix(),
10 statusCodes: { 11 statusCodes: {
11 exclude: [ 404, 403 ] 12 exclude: [
13 HttpStatusCode.FORBIDDEN_403,
14 HttpStatusCode.NOT_FOUND_404
15 ]
12 } 16 }
13} 17}
14 18
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 9834f714b..8bc37b0ab 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -8,6 +8,7 @@ import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/vid
8import { 8import {
9 isBooleanValid, 9 isBooleanValid,
10 isDateValid, 10 isDateValid,
11 isFileFieldValid,
11 isIdOrUUIDValid, 12 isIdOrUUIDValid,
12 isIdValid, 13 isIdValid,
13 isUUIDValid, 14 isUUIDValid,
@@ -22,7 +23,8 @@ import {
22 isScheduleVideoUpdatePrivacyValid, 23 isScheduleVideoUpdatePrivacyValid,
23 isVideoCategoryValid, 24 isVideoCategoryValid,
24 isVideoDescriptionValid, 25 isVideoDescriptionValid,
25 isVideoFile, 26 isVideoFileMimeTypeValid,
27 isVideoFileSizeValid,
26 isVideoFilterValid, 28 isVideoFilterValid,
27 isVideoImage, 29 isVideoImage,
28 isVideoLanguageValid, 30 isVideoLanguageValid,
@@ -55,11 +57,11 @@ import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-c
55 57
56const videosAddValidator = getCommonVideoEditAttributes().concat([ 58const videosAddValidator = getCommonVideoEditAttributes().concat([
57 body('videofile') 59 body('videofile')
58 .custom((value, { req }) => isVideoFile(req.files)).withMessage( 60 .custom((value, { req }) => isFileFieldValid(req.files, 'videofile'))
59 'This file is not supported or too large. Please, make sure it is of the following type: ' + 61 .withMessage('Should have a file'),
60 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') 62 body('name')
61 ), 63 .custom(isVideoNameValid)
62 body('name').custom(isVideoNameValid).withMessage('Should have a valid name'), 64 .withMessage('Should have a valid name'),
63 body('channelId') 65 body('channelId')
64 .customSanitizer(toIntOrNull) 66 .customSanitizer(toIntOrNull)
65 .custom(isIdValid).withMessage('Should have correct video channel id'), 67 .custom(isIdValid).withMessage('Should have correct video channel id'),
@@ -75,8 +77,27 @@ const videosAddValidator = getCommonVideoEditAttributes().concat([
75 77
76 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 78 if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
77 79
80 if (!isVideoFileMimeTypeValid(req.files)) {
81 res.status(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415)
82 .json({
83 error: 'This file is not supported. Please, make sure it is of the following type: ' +
84 CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ')
85 })
86
87 return cleanUpReqFiles(req)
88 }
89
90 if (!isVideoFileSizeValid(videoFile.size.toString())) {
91 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413)
92 .json({
93 error: 'This file is too large.'
94 })
95
96 return cleanUpReqFiles(req)
97 }
98
78 if (await isAbleToUploadVideo(user.id, videoFile.size) === false) { 99 if (await isAbleToUploadVideo(user.id, videoFile.size) === false) {
79 res.status(HttpStatusCode.FORBIDDEN_403) 100 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413)
80 .json({ error: 'The user video quota is exceeded with this video.' }) 101 .json({ error: 'The user video quota is exceeded with this video.' })
81 102
82 return cleanUpReqFiles(req) 103 return cleanUpReqFiles(req)
@@ -88,8 +109,8 @@ const videosAddValidator = getCommonVideoEditAttributes().concat([
88 duration = await getDurationFromVideoFile(videoFile.path) 109 duration = await getDurationFromVideoFile(videoFile.path)
89 } catch (err) { 110 } catch (err) {
90 logger.error('Invalid input file in videosAddValidator.', { err }) 111 logger.error('Invalid input file in videosAddValidator.', { err })
91 res.status(HttpStatusCode.BAD_REQUEST_400) 112 res.status(HttpStatusCode.UNPROCESSABLE_ENTITY_422)
92 .json({ error: 'Invalid input file.' }) 113 .json({ error: 'Video file unreadable.' })
93 114
94 return cleanUpReqFiles(req) 115 return cleanUpReqFiles(req)
95 } 116 }
@@ -295,7 +316,7 @@ const videosAcceptChangeOwnershipValidator = [
295 const videoChangeOwnership = res.locals.videoChangeOwnership 316 const videoChangeOwnership = res.locals.videoChangeOwnership
296 const isAble = await isAbleToUploadVideo(user.id, videoChangeOwnership.Video.getMaxQualityFile().size) 317 const isAble = await isAbleToUploadVideo(user.id, videoChangeOwnership.Video.getMaxQualityFile().size)
297 if (isAble === false) { 318 if (isAble === false) {
298 res.status(HttpStatusCode.FORBIDDEN_403) 319 res.status(HttpStatusCode.PAYLOAD_TOO_LARGE_413)
299 .json({ error: 'The user video quota is exceeded with this video.' }) 320 .json({ error: 'The user video quota is exceeded with this video.' })
300 321
301 return 322 return
diff --git a/server/tests/api/activitypub/client.ts b/server/tests/api/activitypub/client.ts
index d16f05108..b6c538e19 100644
--- a/server/tests/api/activitypub/client.ts
+++ b/server/tests/api/activitypub/client.ts
@@ -11,6 +11,7 @@ import {
11 setAccessTokensToServers, 11 setAccessTokensToServers,
12 uploadVideo 12 uploadVideo
13} from '../../../../shared/extra-utils' 13} from '../../../../shared/extra-utils'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
14 15
15const expect = chai.expect 16const expect = chai.expect
16 17
@@ -53,7 +54,7 @@ describe('Test activitypub', function () {
53 }) 54 })
54 55
55 it('Should redirect to the origin video object', async function () { 56 it('Should redirect to the origin video object', async function () {
56 const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, 302) 57 const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, HttpStatusCode.FOUND_302)
57 58
58 expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) 59 expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID)
59 }) 60 })
diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts
index 232c5d823..c717f1a30 100644
--- a/server/tests/api/activitypub/refresher.ts
+++ b/server/tests/api/activitypub/refresher.ts
@@ -24,6 +24,7 @@ import {
24} from '../../../../shared/extra-utils' 24} from '../../../../shared/extra-utils'
25import { getAccount } from '../../../../shared/extra-utils/users/accounts' 25import { getAccount } from '../../../../shared/extra-utils/users/accounts'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos' 26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos'
27import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
27 28
28describe('Test AP refresher', function () { 29describe('Test AP refresher', function () {
29 let servers: ServerInfo[] = [] 30 let servers: ServerInfo[] = []
@@ -86,8 +87,8 @@ describe('Test AP refresher', function () {
86 87
87 await waitJobs(servers) 88 await waitJobs(servers)
88 89
89 await getVideo(servers[0].url, videoUUID1, 404) 90 await getVideo(servers[0].url, videoUUID1, HttpStatusCode.NOT_FOUND_404)
90 await getVideo(servers[0].url, videoUUID2, 200) 91 await getVideo(servers[0].url, videoUUID2, HttpStatusCode.OK_200)
91 }) 92 })
92 93
93 it('Should not update a remote video if the remote instance is down', async function () { 94 it('Should not update a remote video if the remote instance is down', async function () {
@@ -106,7 +107,7 @@ describe('Test AP refresher', function () {
106 107
107 await reRunServer(servers[1]) 108 await reRunServer(servers[1])
108 109
109 await getVideo(servers[0].url, videoUUID3, 200) 110 await getVideo(servers[0].url, videoUUID3, HttpStatusCode.OK_200)
110 }) 111 })
111 }) 112 })
112 113
@@ -126,8 +127,8 @@ describe('Test AP refresher', function () {
126 127
127 await waitJobs(servers) 128 await waitJobs(servers)
128 129
129 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port, 200) 130 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port, HttpStatusCode.OK_200)
130 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port, 404) 131 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port, HttpStatusCode.NOT_FOUND_404)
131 }) 132 })
132 }) 133 })
133 134
@@ -146,8 +147,8 @@ describe('Test AP refresher', function () {
146 147
147 await waitJobs(servers) 148 await waitJobs(servers)
148 149
149 await getVideoPlaylist(servers[0].url, playlistUUID1, 200) 150 await getVideoPlaylist(servers[0].url, playlistUUID1, HttpStatusCode.OK_200)
150 await getVideoPlaylist(servers[0].url, playlistUUID2, 404) 151 await getVideoPlaylist(servers[0].url, playlistUUID2, HttpStatusCode.NOT_FOUND_404)
151 }) 152 })
152 }) 153 })
153 154
diff --git a/server/tests/api/check-params/accounts.ts b/server/tests/api/check-params/accounts.ts
index c29af7cd7..d1712cff6 100644
--- a/server/tests/api/check-params/accounts.ts
+++ b/server/tests/api/check-params/accounts.ts
@@ -9,6 +9,7 @@ import {
9 checkBadStartPagination 9 checkBadStartPagination
10} from '../../../../shared/extra-utils/requests/check-api-params' 10} from '../../../../shared/extra-utils/requests/check-api-params'
11import { getAccount } from '../../../../shared/extra-utils/users/accounts' 11import { getAccount } from '../../../../shared/extra-utils/users/accounts'
12import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
12 13
13describe('Test accounts API validators', function () { 14describe('Test accounts API validators', function () {
14 const path = '/api/v1/accounts/' 15 const path = '/api/v1/accounts/'
@@ -38,7 +39,7 @@ describe('Test accounts API validators', function () {
38 39
39 describe('When getting an account', function () { 40 describe('When getting an account', function () {
40 it('Should return 404 with a non existing name', async function () { 41 it('Should return 404 with a non existing name', async function () {
41 await getAccount(server.url, 'arfaze', 404) 42 await getAccount(server.url, 'arfaze', HttpStatusCode.NOT_FOUND_404)
42 }) 43 })
43 }) 44 })
44 45
diff --git a/server/tests/api/check-params/contact-form.ts b/server/tests/api/check-params/contact-form.ts
index b2126b9b0..c7f9c1b47 100644
--- a/server/tests/api/check-params/contact-form.ts
+++ b/server/tests/api/check-params/contact-form.ts
@@ -5,6 +5,7 @@ import 'mocha'
5import { cleanupTests, flushAndRunServer, immutableAssign, killallServers, reRunServer, ServerInfo } from '../../../../shared/extra-utils' 5import { cleanupTests, flushAndRunServer, immutableAssign, killallServers, reRunServer, ServerInfo } from '../../../../shared/extra-utils'
6import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 6import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form'
7import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 7import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8 9
9describe('Test contact form API validators', function () { 10describe('Test contact form API validators', function () {
10 let server: ServerInfo 11 let server: ServerInfo
@@ -29,7 +30,7 @@ describe('Test contact form API validators', function () {
29 }) 30 })
30 31
31 it('Should not accept a contact form if emails are disabled', async function () { 32 it('Should not accept a contact form if emails are disabled', async function () {
32 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 409 })) 33 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 }))
33 }) 34 })
34 35
35 it('Should not accept a contact form if it is disabled in the configuration', async function () { 36 it('Should not accept a contact form if it is disabled in the configuration', async function () {
@@ -39,7 +40,7 @@ describe('Test contact form API validators', function () {
39 40
40 // Contact form is disabled 41 // Contact form is disabled
41 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } }) 42 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } })
42 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 409 })) 43 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 }))
43 }) 44 })
44 45
45 it('Should not accept a contact form if from email is invalid', async function () { 46 it('Should not accept a contact form if from email is invalid', async function () {
@@ -50,21 +51,57 @@ describe('Test contact form API validators', function () {
50 // Email & contact form enabled 51 // Email & contact form enabled
51 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort } }) 52 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort } })
52 53
53 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromEmail: 'badEmail' })) 54 await sendContactForm(immutableAssign(defaultBody, {
54 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromEmail: 'badEmail@' })) 55 url: server.url,
55 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromEmail: undefined })) 56 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
57 fromEmail: 'badEmail'
58 }))
59 await sendContactForm(immutableAssign(defaultBody, {
60 url: server.url,
61 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
62 fromEmail: 'badEmail@'
63 }))
64 await sendContactForm(immutableAssign(defaultBody, {
65 url: server.url,
66 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
67 fromEmail: undefined
68 }))
56 }) 69 })
57 70
58 it('Should not accept a contact form if from name is invalid', async function () { 71 it('Should not accept a contact form if from name is invalid', async function () {
59 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromName: 'name'.repeat(100) })) 72 await sendContactForm(immutableAssign(defaultBody, {
60 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromName: '' })) 73 url: server.url,
61 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, fromName: undefined })) 74 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
75 fromName: 'name'.repeat(100)
76 }))
77 await sendContactForm(immutableAssign(defaultBody, {
78 url: server.url,
79 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
80 fromName: ''
81 }))
82 await sendContactForm(immutableAssign(defaultBody, {
83 url: server.url,
84 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
85 fromName: undefined
86 }))
62 }) 87 })
63 88
64 it('Should not accept a contact form if body is invalid', async function () { 89 it('Should not accept a contact form if body is invalid', async function () {
65 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, body: 'body'.repeat(5000) })) 90 await sendContactForm(immutableAssign(defaultBody, {
66 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, body: 'a' })) 91 url: server.url,
67 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: 400, body: undefined })) 92 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
93 body: 'body'.repeat(5000)
94 }))
95 await sendContactForm(immutableAssign(defaultBody, {
96 url: server.url,
97 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
98 body: 'a'
99 }))
100 await sendContactForm(immutableAssign(defaultBody, {
101 url: server.url,
102 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
103 body: undefined
104 }))
68 }) 105 })
69 106
70 it('Should accept a contact form with the correct parameters', async function () { 107 it('Should accept a contact form with the correct parameters', async function () {
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 21ace36aa..0a13f5b67 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -1102,7 +1102,7 @@ describe('Test users API validators', function () {
1102 videoQuota: 42 1102 videoQuota: 42
1103 }) 1103 })
1104 1104
1105 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.FORBIDDEN_403) 1105 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413)
1106 }) 1106 })
1107 1107
1108 it('Should fail with a registered user having too many videos', async function () { 1108 it('Should fail with a registered user having too many videos', async function () {
@@ -1120,7 +1120,7 @@ describe('Test users API validators', function () {
1120 await uploadVideo(server.url, userAccessToken, videoAttributes) 1120 await uploadVideo(server.url, userAccessToken, videoAttributes)
1121 await uploadVideo(server.url, userAccessToken, videoAttributes) 1121 await uploadVideo(server.url, userAccessToken, videoAttributes)
1122 await uploadVideo(server.url, userAccessToken, videoAttributes) 1122 await uploadVideo(server.url, userAccessToken, videoAttributes)
1123 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.FORBIDDEN_403) 1123 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.PAYLOAD_TOO_LARGE_413)
1124 }) 1124 })
1125 1125
1126 it('Should fail to import with HTTP/Torrent/magnet', async function () { 1126 it('Should fail to import with HTTP/Torrent/magnet', async function () {
@@ -1151,7 +1151,7 @@ describe('Test users API validators', function () {
1151 }) 1151 })
1152 1152
1153 describe('When having a daily video quota', function () { 1153 describe('When having a daily video quota', function () {
1154 it('Should fail with a user having too many videos', async function () { 1154 it('Should fail with a user having too many videos daily', async function () {
1155 await updateUser({ 1155 await updateUser({
1156 url: server.url, 1156 url: server.url,
1157 userId: rootId, 1157 userId: rootId,
@@ -1159,7 +1159,7 @@ describe('Test users API validators', function () {
1159 videoQuotaDaily: 42 1159 videoQuotaDaily: 42
1160 }) 1160 })
1161 1161
1162 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.FORBIDDEN_403) 1162 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413)
1163 }) 1163 })
1164 }) 1164 })
1165 1165
@@ -1173,7 +1173,7 @@ describe('Test users API validators', function () {
1173 videoQuotaDaily: 1024 * 1024 * 1024 1173 videoQuotaDaily: 1024 * 1024 * 1024
1174 }) 1174 })
1175 1175
1176 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.FORBIDDEN_403) 1176 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413)
1177 }) 1177 })
1178 1178
1179 it('Should fail if exceeding daily quota', async function () { 1179 it('Should fail if exceeding daily quota', async function () {
@@ -1185,7 +1185,7 @@ describe('Test users API validators', function () {
1185 videoQuotaDaily: 42 1185 videoQuotaDaily: 42
1186 }) 1186 })
1187 1187
1188 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.FORBIDDEN_403) 1188 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413)
1189 }) 1189 })
1190 }) 1190 })
1191 1191
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index d60546917..5faba82c4 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -348,12 +348,26 @@ describe('Test videos API validator', function () {
348 let attaches = { 348 let attaches = {
349 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm') 349 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm')
350 } 350 }
351 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) 351 await makeUploadRequest({
352 url: server.url,
353 path: path + '/upload',
354 token: server.accessToken,
355 fields,
356 attaches,
357 statusCodeExpected: HttpStatusCode.UNPROCESSABLE_ENTITY_422
358 })
352 359
353 attaches = { 360 attaches = {
354 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv') 361 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv')
355 } 362 }
356 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) 363 await makeUploadRequest({
364 url: server.url,
365 path: path + '/upload',
366 token: server.accessToken,
367 fields,
368 attaches,
369 statusCodeExpected: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415
370 })
357 }) 371 })
358 372
359 it('Should fail with an incorrect thumbnail file', async function () { 373 it('Should fail with an incorrect thumbnail file', async function () {
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts
index 3ffa0c093..e300ec345 100644
--- a/server/tests/api/live/live-save-replay.ts
+++ b/server/tests/api/live/live-save-replay.ts
@@ -25,6 +25,7 @@ import {
25 waitJobs, 25 waitJobs,
26 waitUntilLiveStarts 26 waitUntilLiveStarts
27} from '../../../../shared/extra-utils' 27} from '../../../../shared/extra-utils'
28import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
28 29
29const expect = chai.expect 30const expect = chai.expect
30 31
@@ -118,7 +119,7 @@ describe('Save replay setting', function () {
118 119
119 await waitJobs(servers) 120 await waitJobs(servers)
120 121
121 await checkVideosExist(liveVideoUUID, false, 200) 122 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
122 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) 123 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
123 }) 124 })
124 125
@@ -130,7 +131,7 @@ describe('Save replay setting', function () {
130 131
131 await waitJobs(servers) 132 await waitJobs(servers)
132 133
133 await checkVideosExist(liveVideoUUID, true, 200) 134 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
134 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) 135 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
135 }) 136 })
136 137
@@ -142,7 +143,7 @@ describe('Save replay setting', function () {
142 await waitJobs(servers) 143 await waitJobs(servers)
143 144
144 // Live still exist, but cannot be played anymore 145 // Live still exist, but cannot be played anymore
145 await checkVideosExist(liveVideoUUID, false, 200) 146 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
146 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED) 147 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
147 148
148 // No resolutions saved since we did not save replay 149 // No resolutions saved since we did not save replay
@@ -158,7 +159,7 @@ describe('Save replay setting', function () {
158 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) 159 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
159 160
160 await waitJobs(servers) 161 await waitJobs(servers)
161 await checkVideosExist(liveVideoUUID, true, 200) 162 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
162 163
163 await Promise.all([ 164 await Promise.all([
164 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 165 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true),
@@ -169,8 +170,8 @@ describe('Save replay setting', function () {
169 170
170 await checkVideosExist(liveVideoUUID, false) 171 await checkVideosExist(liveVideoUUID, false)
171 172
172 await getVideo(servers[0].url, liveVideoUUID, 401) 173 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401)
173 await getVideo(servers[1].url, liveVideoUUID, 404) 174 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
174 175
175 await checkLiveCleanup(servers[0], liveVideoUUID, []) 176 await checkLiveCleanup(servers[0], liveVideoUUID, [])
176 }) 177 })
@@ -184,7 +185,7 @@ describe('Save replay setting', function () {
184 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) 185 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
185 186
186 await waitJobs(servers) 187 await waitJobs(servers)
187 await checkVideosExist(liveVideoUUID, true, 200) 188 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
188 189
189 await Promise.all([ 190 await Promise.all([
190 testFfmpegStreamError(ffmpegCommand, true), 191 testFfmpegStreamError(ffmpegCommand, true),
@@ -193,7 +194,7 @@ describe('Save replay setting', function () {
193 194
194 await waitJobs(servers) 195 await waitJobs(servers)
195 196
196 await checkVideosExist(liveVideoUUID, false, 404) 197 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
197 await checkLiveCleanup(servers[0], liveVideoUUID, []) 198 await checkLiveCleanup(servers[0], liveVideoUUID, [])
198 }) 199 })
199 }) 200 })
@@ -207,7 +208,7 @@ describe('Save replay setting', function () {
207 208
208 await waitJobs(servers) 209 await waitJobs(servers)
209 210
210 await checkVideosExist(liveVideoUUID, false, 200) 211 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
211 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) 212 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
212 }) 213 })
213 214
@@ -219,7 +220,7 @@ describe('Save replay setting', function () {
219 220
220 await waitJobs(servers) 221 await waitJobs(servers)
221 222
222 await checkVideosExist(liveVideoUUID, true, 200) 223 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
223 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) 224 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
224 }) 225 })
225 226
@@ -231,7 +232,7 @@ describe('Save replay setting', function () {
231 await waitJobs(servers) 232 await waitJobs(servers)
232 233
233 // Live has been transcoded 234 // Live has been transcoded
234 await checkVideosExist(liveVideoUUID, true, 200) 235 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
235 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) 236 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
236 }) 237 })
237 238
@@ -261,7 +262,7 @@ describe('Save replay setting', function () {
261 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) 262 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
262 263
263 await waitJobs(servers) 264 await waitJobs(servers)
264 await checkVideosExist(liveVideoUUID, true, 200) 265 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
265 266
266 await Promise.all([ 267 await Promise.all([
267 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 268 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true),
@@ -272,8 +273,8 @@ describe('Save replay setting', function () {
272 273
273 await checkVideosExist(liveVideoUUID, false) 274 await checkVideosExist(liveVideoUUID, false)
274 275
275 await getVideo(servers[0].url, liveVideoUUID, 401) 276 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401)
276 await getVideo(servers[1].url, liveVideoUUID, 404) 277 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
277 278
278 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) 279 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
279 }) 280 })
@@ -287,7 +288,7 @@ describe('Save replay setting', function () {
287 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) 288 await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID)
288 289
289 await waitJobs(servers) 290 await waitJobs(servers)
290 await checkVideosExist(liveVideoUUID, true, 200) 291 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
291 292
292 await Promise.all([ 293 await Promise.all([
293 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID), 294 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID),
@@ -296,7 +297,7 @@ describe('Save replay setting', function () {
296 297
297 await waitJobs(servers) 298 await waitJobs(servers)
298 299
299 await checkVideosExist(liveVideoUUID, false, 404) 300 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
300 await checkLiveCleanup(servers[0], liveVideoUUID, []) 301 await checkLiveCleanup(servers[0], liveVideoUUID, [])
301 }) 302 })
302 }) 303 })
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index d784650b5..fdfc6105f 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -44,6 +44,7 @@ import {
44 waitUntilLiveStarts, 44 waitUntilLiveStarts,
45 waitUntilLog 45 waitUntilLog
46} from '../../../../shared/extra-utils' 46} from '../../../../shared/extra-utils'
47import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
47 48
48const expect = chai.expect 49const expect = chai.expect
49 50
@@ -164,8 +165,8 @@ describe('Test live', function () {
164 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 165 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
165 expect(video.nsfw).to.be.true 166 expect(video.nsfw).to.be.true
166 167
167 await makeRawRequest(server.url + video.thumbnailPath, 200) 168 await makeRawRequest(server.url + video.thumbnailPath, HttpStatusCode.OK_200)
168 await makeRawRequest(server.url + video.previewPath, 200) 169 await makeRawRequest(server.url + video.previewPath, HttpStatusCode.OK_200)
169 } 170 }
170 }) 171 })
171 172
@@ -179,7 +180,7 @@ describe('Test live', function () {
179 }) 180 })
180 181
181 it('Should not be able to update a live of another server', async function () { 182 it('Should not be able to update a live of another server', async function () {
182 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, 403) 183 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, HttpStatusCode.FORBIDDEN_403)
183 }) 184 })
184 185
185 it('Should update the live', async function () { 186 it('Should update the live', async function () {
@@ -215,8 +216,8 @@ describe('Test live', function () {
215 216
216 it('Should have the live deleted', async function () { 217 it('Should have the live deleted', async function () {
217 for (const server of servers) { 218 for (const server of servers) {
218 await getVideo(server.url, liveVideoUUID, 404) 219 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
219 await getLive(server.url, server.accessToken, liveVideoUUID, 404) 220 await getLive(server.url, server.accessToken, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
220 } 221 }
221 }) 222 })
222 }) 223 })
@@ -430,8 +431,8 @@ describe('Test live', function () {
430 expect(video.files).to.have.lengthOf(0) 431 expect(video.files).to.have.lengthOf(0)
431 432
432 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS) 433 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
433 await makeRawRequest(hlsPlaylist.playlistUrl, 200) 434 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200)
434 await makeRawRequest(hlsPlaylist.segmentsSha256Url, 200) 435 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200)
435 436
436 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length) 437 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length)
437 438
@@ -455,8 +456,8 @@ describe('Test live', function () {
455 456
456 expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height]) 457 expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height])
457 458
458 await makeRawRequest(file.torrentUrl, 200) 459 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
459 await makeRawRequest(file.fileUrl, 200) 460 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
460 } 461 }
461 } 462 }
462 }) 463 })
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index eb51d8909..a505b8ede 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -21,6 +21,7 @@ import {
21 uploadVideo 21 uploadVideo
22} from '../../../../shared/extra-utils' 22} from '../../../../shared/extra-utils'
23import { ServerConfig } from '../../../../shared/models' 23import { ServerConfig } from '../../../../shared/models'
24import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 25
25const expect = chai.expect 26const expect = chai.expect
26 27
@@ -237,8 +238,8 @@ describe('Test config', function () {
237 expect(data.video.file.extensions).to.contain('.webm') 238 expect(data.video.file.extensions).to.contain('.webm')
238 expect(data.video.file.extensions).to.contain('.ogv') 239 expect(data.video.file.extensions).to.contain('.ogv')
239 240
240 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 400) 241 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415)
241 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 400) 242 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415)
242 243
243 expect(data.contactForm.enabled).to.be.true 244 expect(data.contactForm.enabled).to.be.true
244 }) 245 })
@@ -427,8 +428,8 @@ describe('Test config', function () {
427 expect(data.video.file.extensions).to.contain('.ogg') 428 expect(data.video.file.extensions).to.contain('.ogg')
428 expect(data.video.file.extensions).to.contain('.flac') 429 expect(data.video.file.extensions).to.contain('.flac')
429 430
430 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 200) 431 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.OK_200)
431 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 200) 432 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.OK_200)
432 }) 433 })
433 434
434 it('Should have the configuration updated after a restart', async function () { 435 it('Should have the configuration updated after a restart', async function () {
diff --git a/server/tests/api/server/contact-form.ts b/server/tests/api/server/contact-form.ts
index c0965d9d1..9b4af1915 100644
--- a/server/tests/api/server/contact-form.ts
+++ b/server/tests/api/server/contact-form.ts
@@ -6,6 +6,7 @@ import { cleanupTests, flushAndRunServer, ServerInfo, setAccessTokensToServers,
6import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 6import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
7import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 7import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
8import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 8import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9 10
10const expect = chai.expect 11const expect = chai.expect
11 12
@@ -67,7 +68,7 @@ describe('Test contact form', function () {
67 body: 'my super message', 68 body: 'my super message',
68 fromName: 'Super toto', 69 fromName: 'Super toto',
69 subject: 'my subject', 70 subject: 'my subject',
70 expectedStatus: 403 71 expectedStatus: HttpStatusCode.FORBIDDEN_403
71 }) 72 })
72 }) 73 })
73 74
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts
index 53f96a94f..17d9e902c 100644
--- a/server/tests/api/server/email.ts
+++ b/server/tests/api/server/email.ts
@@ -22,6 +22,7 @@ import {
22} from '../../../../shared/extra-utils' 22} from '../../../../shared/extra-utils'
23import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 23import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
24import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 24import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
25 26
26const expect = chai.expect 27const expect = chai.expect
27 28
@@ -116,7 +117,7 @@ describe('Test emails', function () {
116 }) 117 })
117 118
118 it('Should not reset the password with an invalid verification string', async function () { 119 it('Should not reset the password with an invalid verification string', async function () {
119 await resetPassword(server.url, userId, verificationString + 'b', 'super_password2', 403) 120 await resetPassword(server.url, userId, verificationString + 'b', 'super_password2', HttpStatusCode.FORBIDDEN_403)
120 }) 121 })
121 122
122 it('Should reset the password', async function () { 123 it('Should reset the password', async function () {
@@ -124,7 +125,7 @@ describe('Test emails', function () {
124 }) 125 })
125 126
126 it('Should not reset the password with the same verification string', async function () { 127 it('Should not reset the password with the same verification string', async function () {
127 await resetPassword(server.url, userId, verificationString, 'super_password3', 403) 128 await resetPassword(server.url, userId, verificationString, 'super_password3', HttpStatusCode.FORBIDDEN_403)
128 }) 129 })
129 130
130 it('Should login with this new password', async function () { 131 it('Should login with this new password', async function () {
@@ -169,7 +170,7 @@ describe('Test emails', function () {
169 }) 170 })
170 171
171 it('Should not reset the password with an invalid verification string', async function () { 172 it('Should not reset the password with an invalid verification string', async function () {
172 await resetPassword(server.url, userId2, verificationString2 + 'c', 'newly_created_password', 403) 173 await resetPassword(server.url, userId2, verificationString2 + 'c', 'newly_created_password', HttpStatusCode.FORBIDDEN_403)
173 }) 174 })
174 175
175 it('Should reset the password', async function () { 176 it('Should reset the password', async function () {
@@ -210,7 +211,7 @@ describe('Test emails', function () {
210 this.timeout(10000) 211 this.timeout(10000)
211 212
212 const reason = 'my super bad reason' 213 const reason = 'my super bad reason'
213 await blockUser(server.url, userId, server.accessToken, 204, reason) 214 await blockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204, reason)
214 215
215 await waitJobs(server) 216 await waitJobs(server)
216 expect(emails).to.have.lengthOf(4) 217 expect(emails).to.have.lengthOf(4)
@@ -228,7 +229,7 @@ describe('Test emails', function () {
228 it('Should send the notification email when unblocking a user', async function () { 229 it('Should send the notification email when unblocking a user', async function () {
229 this.timeout(10000) 230 this.timeout(10000)
230 231
231 await unblockUser(server.url, userId, server.accessToken, 204) 232 await unblockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204)
232 233
233 await waitJobs(server) 234 await waitJobs(server)
234 expect(emails).to.have.lengthOf(5) 235 expect(emails).to.have.lengthOf(5)
@@ -317,7 +318,7 @@ describe('Test emails', function () {
317 }) 318 })
318 319
319 it('Should not verify the email with an invalid verification string', async function () { 320 it('Should not verify the email with an invalid verification string', async function () {
320 await verifyEmail(server.url, userId, verificationString + 'b', false, 403) 321 await verifyEmail(server.url, userId, verificationString + 'b', false, HttpStatusCode.FORBIDDEN_403)
321 }) 322 })
322 323
323 it('Should verify the email', async function () { 324 it('Should verify the email', async function () {
diff --git a/server/tests/api/server/follow-constraints.ts b/server/tests/api/server/follow-constraints.ts
index a73440286..0846b04f4 100644
--- a/server/tests/api/server/follow-constraints.ts
+++ b/server/tests/api/server/follow-constraints.ts
@@ -17,6 +17,7 @@ import {
17import { unfollow } from '../../../../shared/extra-utils/server/follows' 17import { unfollow } from '../../../../shared/extra-utils/server/follows'
18import { userLogin } from '../../../../shared/extra-utils/users/login' 18import { userLogin } from '../../../../shared/extra-utils/users/login'
19import { createUser } from '../../../../shared/extra-utils/users/users' 19import { createUser } from '../../../../shared/extra-utils/users/users'
20import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
20 21
21const expect = chai.expect 22const expect = chai.expect
22 23
@@ -58,11 +59,11 @@ describe('Test follow constraints', function () {
58 describe('With an unlogged user', function () { 59 describe('With an unlogged user', function () {
59 60
60 it('Should get the local video', async function () { 61 it('Should get the local video', async function () {
61 await getVideo(servers[0].url, video1UUID, 200) 62 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200)
62 }) 63 })
63 64
64 it('Should get the remote video', async function () { 65 it('Should get the remote video', async function () {
65 await getVideo(servers[0].url, video2UUID, 200) 66 await getVideo(servers[0].url, video2UUID, HttpStatusCode.OK_200)
66 }) 67 })
67 68
68 it('Should list local account videos', async function () { 69 it('Should list local account videos', async function () {
@@ -98,11 +99,11 @@ describe('Test follow constraints', function () {
98 99
99 describe('With a logged user', function () { 100 describe('With a logged user', function () {
100 it('Should get the local video', async function () { 101 it('Should get the local video', async function () {
101 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, 200) 102 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200)
102 }) 103 })
103 104
104 it('Should get the remote video', async function () { 105 it('Should get the remote video', async function () {
105 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, 200) 106 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200)
106 }) 107 })
107 108
108 it('Should list local account videos', async function () { 109 it('Should list local account videos', async function () {
@@ -148,11 +149,11 @@ describe('Test follow constraints', function () {
148 describe('With an unlogged user', function () { 149 describe('With an unlogged user', function () {
149 150
150 it('Should get the local video', async function () { 151 it('Should get the local video', async function () {
151 await getVideo(servers[0].url, video1UUID, 200) 152 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200)
152 }) 153 })
153 154
154 it('Should not get the remote video', async function () { 155 it('Should not get the remote video', async function () {
155 await getVideo(servers[0].url, video2UUID, 403) 156 await getVideo(servers[0].url, video2UUID, HttpStatusCode.FORBIDDEN_403)
156 }) 157 })
157 158
158 it('Should list local account videos', async function () { 159 it('Should list local account videos', async function () {
@@ -188,11 +189,11 @@ describe('Test follow constraints', function () {
188 189
189 describe('With a logged user', function () { 190 describe('With a logged user', function () {
190 it('Should get the local video', async function () { 191 it('Should get the local video', async function () {
191 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, 200) 192 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200)
192 }) 193 })
193 194
194 it('Should get the remote video', async function () { 195 it('Should get the remote video', async function () {
195 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, 200) 196 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200)
196 }) 197 })
197 198
198 it('Should list local account videos', async function () { 199 it('Should list local account videos', async function () {
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index 2cf6e15ad..043754e70 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -33,6 +33,7 @@ import {
33 getVideoCommentThreads, 33 getVideoCommentThreads,
34 getVideoThreadComments 34 getVideoThreadComments
35} from '../../../../shared/extra-utils/videos/video-comments' 35} from '../../../../shared/extra-utils/videos/video-comments'
36import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
36 37
37const expect = chai.expect 38const expect = chai.expect
38 39
@@ -352,7 +353,7 @@ describe('Test handle downs', function () {
352 } 353 }
353 354
354 for (const id of videoIdsServer1) { 355 for (const id of videoIdsServer1) {
355 await getVideo(servers[1].url, id, 403) 356 await getVideo(servers[1].url, id, HttpStatusCode.FORBIDDEN_403)
356 } 357 }
357 }) 358 })
358 359
diff --git a/server/tests/api/server/no-client.ts b/server/tests/api/server/no-client.ts
index d0450aba0..d589f51f3 100644
--- a/server/tests/api/server/no-client.ts
+++ b/server/tests/api/server/no-client.ts
@@ -2,6 +2,7 @@ import 'mocha'
2import * as request from 'supertest' 2import * as request from 'supertest'
3import { ServerInfo } from '../../../../shared/extra-utils' 3import { ServerInfo } from '../../../../shared/extra-utils'
4import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers' 4import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
5 6
6describe('Start and stop server without web client routes', function () { 7describe('Start and stop server without web client routes', function () {
7 let server: ServerInfo 8 let server: ServerInfo
@@ -16,7 +17,7 @@ describe('Start and stop server without web client routes', function () {
16 const req = request(server.url) 17 const req = request(server.url)
17 .get('/') 18 .get('/')
18 19
19 return req.expect(404) 20 return req.expect(HttpStatusCode.NOT_FOUND_404)
20 }) 21 })
21 22
22 after(async function () { 23 after(async function () {
diff --git a/server/tests/api/server/reverse-proxy.ts b/server/tests/api/server/reverse-proxy.ts
index d0d79c4f6..17d1ee4a5 100644
--- a/server/tests/api/server/reverse-proxy.ts
+++ b/server/tests/api/server/reverse-proxy.ts
@@ -4,6 +4,7 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { cleanupTests, getVideo, registerUser, uploadVideo, userLogin, viewVideo, wait } from '../../../../shared/extra-utils' 5import { cleanupTests, getVideo, registerUser, uploadVideo, userLogin, viewVideo, wait } from '../../../../shared/extra-utils'
6import { flushAndRunServer, setAccessTokensToServers } from '../../../../shared/extra-utils/index' 6import { flushAndRunServer, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7 8
8const expect = chai.expect 9const expect = chai.expect
9 10
@@ -56,8 +57,8 @@ describe('Test application behind a reverse proxy', function () {
56 it('Should view a video 2 times with the X-Forwarded-For header set', async function () { 57 it('Should view a video 2 times with the X-Forwarded-For header set', async function () {
57 this.timeout(20000) 58 this.timeout(20000)
58 59
59 await viewVideo(server.url, videoId, 204, '0.0.0.1,127.0.0.1') 60 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.1,127.0.0.1')
60 await viewVideo(server.url, videoId, 204, '0.0.0.2,127.0.0.1') 61 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.2,127.0.0.1')
61 62
62 // Wait the repeatable job 63 // Wait the repeatable job
63 await wait(8000) 64 await wait(8000)
@@ -69,8 +70,8 @@ describe('Test application behind a reverse proxy', function () {
69 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () { 70 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () {
70 this.timeout(20000) 71 this.timeout(20000)
71 72
72 await viewVideo(server.url, videoId, 204, '0.0.0.4,0.0.0.3,::ffff:127.0.0.1') 73 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.4,0.0.0.3,::ffff:127.0.0.1')
73 await viewVideo(server.url, videoId, 204, '0.0.0.5,0.0.0.3,127.0.0.1') 74 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.5,0.0.0.3,127.0.0.1')
74 75
75 // Wait the repeatable job 76 // Wait the repeatable job
76 await wait(8000) 77 await wait(8000)
@@ -82,8 +83,8 @@ describe('Test application behind a reverse proxy', function () {
82 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () { 83 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () {
83 this.timeout(20000) 84 this.timeout(20000)
84 85
85 await viewVideo(server.url, videoId, 204, '0.0.0.8,0.0.0.6,127.0.0.1') 86 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.6,127.0.0.1')
86 await viewVideo(server.url, videoId, 204, '0.0.0.8,0.0.0.7,127.0.0.1') 87 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.7,127.0.0.1')
87 88
88 // Wait the repeatable job 89 // Wait the repeatable job
89 await wait(8000) 90 await wait(8000)
@@ -96,10 +97,10 @@ describe('Test application behind a reverse proxy', function () {
96 const user = { username: 'root', password: 'fail' } 97 const user = { username: 'root', password: 'fail' }
97 98
98 for (let i = 0; i < 19; i++) { 99 for (let i = 0; i < 19; i++) {
99 await userLogin(server, user, 400) 100 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400)
100 } 101 }
101 102
102 await userLogin(server, user, 429) 103 await userLogin(server, user, HttpStatusCode.TOO_MANY_REQUESTS_429)
103 }) 104 })
104 105
105 it('Should rate limit signup', async function () { 106 it('Should rate limit signup', async function () {
@@ -111,7 +112,7 @@ describe('Test application behind a reverse proxy', function () {
111 } 112 }
112 } 113 }
113 114
114 await registerUser(server.url, 'test42', 'password', 429) 115 await registerUser(server.url, 'test42', 'password', HttpStatusCode.TOO_MANY_REQUESTS_429)
115 }) 116 })
116 117
117 it('Should not rate limit failed signup', async function () { 118 it('Should not rate limit failed signup', async function () {
@@ -120,10 +121,10 @@ describe('Test application behind a reverse proxy', function () {
120 await wait(7000) 121 await wait(7000)
121 122
122 for (let i = 0; i < 3; i++) { 123 for (let i = 0; i < 3; i++) {
123 await registerUser(server.url, 'test' + i, 'password', 409) 124 await registerUser(server.url, 'test' + i, 'password', HttpStatusCode.CONFLICT_409)
124 } 125 }
125 126
126 await registerUser(server.url, 'test43', 'password', 204) 127 await registerUser(server.url, 'test43', 'password', HttpStatusCode.NO_CONTENT_204)
127 128
128 }) 129 })
129 130
@@ -140,7 +141,7 @@ describe('Test application behind a reverse proxy', function () {
140 } 141 }
141 } 142 }
142 143
143 await getVideo(server.url, videoId, 429) 144 await getVideo(server.url, videoId, HttpStatusCode.TOO_MANY_REQUESTS_429)
144 }) 145 })
145 146
146 after(async function () { 147 after(async function () {
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index fdd5e33f3..f754df04e 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -41,6 +41,7 @@ import {
41 findCommentId 41 findCommentId
42} from '../../../../shared/extra-utils/videos/video-comments' 42} from '../../../../shared/extra-utils/videos/video-comments'
43import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 43import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
44import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
44 45
45const expect = chai.expect 46const expect = chai.expect
46 47
@@ -999,7 +1000,7 @@ describe('Test multiple servers', function () {
999 expect(res.body.downloadEnabled).to.be.false 1000 expect(res.body.downloadEnabled).to.be.false
1000 1001
1001 const text = 'my super forbidden comment' 1002 const text = 'my super forbidden comment'
1002 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, 409) 1003 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, HttpStatusCode.CONFLICT_409)
1003 } 1004 }
1004 }) 1005 })
1005 }) 1006 })
@@ -1021,7 +1022,7 @@ describe('Test multiple servers', function () {
1021 const filePath = join(__dirname, '..', '..', 'fixtures', 'video_short.webm') 1022 const filePath = join(__dirname, '..', '..', 'fixtures', 'video_short.webm')
1022 1023
1023 await req.attach('videofile', filePath) 1024 await req.attach('videofile', filePath)
1024 .expect(200) 1025 .expect(HttpStatusCode.OK_200)
1025 1026
1026 await waitJobs(servers) 1027 await waitJobs(servers)
1027 1028
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts
index dee6575b9..fad4c8b1f 100644
--- a/server/tests/api/videos/video-change-ownership.ts
+++ b/server/tests/api/videos/video-change-ownership.ts
@@ -23,6 +23,7 @@ import {
23import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 23import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
24import { User } from '../../../../shared/models/users' 24import { User } from '../../../../shared/models/users'
25import { VideoDetails } from '../../../../shared/models/videos' 25import { VideoDetails } from '../../../../shared/models/videos'
26import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 27
27const expect = chai.expect 28const expect = chai.expect
28 29
@@ -140,7 +141,7 @@ describe('Test video change ownership - nominal', function () {
140 it('Should not be possible to refuse the change of ownership from first user', async function () { 141 it('Should not be possible to refuse the change of ownership from first user', async function () {
141 this.timeout(10000) 142 this.timeout(10000)
142 143
143 await refuseChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, 403) 144 await refuseChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, HttpStatusCode.FORBIDDEN_403)
144 }) 145 })
145 146
146 it('Should be possible to refuse the change of ownership from second user', async function () { 147 it('Should be possible to refuse the change of ownership from second user', async function () {
@@ -177,7 +178,7 @@ describe('Test video change ownership - nominal', function () {
177 const secondUserInformationResponse = await getMyUserInformation(servers[0].url, secondUserAccessToken) 178 const secondUserInformationResponse = await getMyUserInformation(servers[0].url, secondUserAccessToken)
178 const secondUserInformation: User = secondUserInformationResponse.body 179 const secondUserInformation: User = secondUserInformationResponse.body
179 const channelId = secondUserInformation.videoChannels[0].id 180 const channelId = secondUserInformation.videoChannels[0].id
180 await acceptChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, channelId, 403) 181 await acceptChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, channelId, HttpStatusCode.FORBIDDEN_403)
181 }) 182 })
182 183
183 it('Should be possible to accept the change of ownership from second user', async function () { 184 it('Should be possible to accept the change of ownership from second user', async function () {
@@ -294,7 +295,14 @@ describe('Test video change ownership - quota too small', function () {
294 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken) 295 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken)
295 const secondUserInformation: User = secondUserInformationResponse.body 296 const secondUserInformation: User = secondUserInformationResponse.body
296 const channelId = secondUserInformation.videoChannels[0].id 297 const channelId = secondUserInformation.videoChannels[0].id
297 await acceptChangeOwnership(server.url, secondUserAccessToken, lastRequestChangeOwnershipId, channelId, 403) 298
299 await acceptChangeOwnership(
300 server.url,
301 secondUserAccessToken,
302 lastRequestChangeOwnershipId,
303 channelId,
304 HttpStatusCode.PAYLOAD_TOO_LARGE_413
305 )
298 }) 306 })
299 307
300 after(async function () { 308 after(async function () {
diff --git a/server/tests/api/videos/video-hls.ts b/server/tests/api/videos/video-hls.ts
index 3a65cc1d2..f3dbbb114 100644
--- a/server/tests/api/videos/video-hls.ts
+++ b/server/tests/api/videos/video-hls.ts
@@ -26,6 +26,7 @@ import {
26import { VideoDetails } from '../../../../shared/models/videos' 26import { VideoDetails } from '../../../../shared/models/videos'
27import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' 27import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
28import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 28import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
29 30
30const expect = chai.expect 31const expect = chai.expect
31 32
@@ -57,8 +58,8 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
57 ) 58 )
58 expect(file.resolution.label).to.equal(resolution + 'p') 59 expect(file.resolution.label).to.equal(resolution + 'p')
59 60
60 await makeRawRequest(file.torrentUrl, 200) 61 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
61 await makeRawRequest(file.fileUrl, 200) 62 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
62 63
63 const torrent = await webtorrentAdd(file.magnetUri, true) 64 const torrent = await webtorrentAdd(file.magnetUri, true)
64 expect(torrent.files).to.be.an('array') 65 expect(torrent.files).to.be.an('array')
@@ -144,8 +145,8 @@ describe('Test HLS videos', function () {
144 await waitJobs(servers) 145 await waitJobs(servers)
145 146
146 for (const server of servers) { 147 for (const server of servers) {
147 await getVideo(server.url, videoUUID, 404) 148 await getVideo(server.url, videoUUID, HttpStatusCode.NOT_FOUND_404)
148 await getVideo(server.url, videoAudioUUID, 404) 149 await getVideo(server.url, videoAudioUUID, HttpStatusCode.NOT_FOUND_404)
149 } 150 }
150 }) 151 })
151 152
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts
index 7d1215990..0a96ea9a0 100644
--- a/server/tests/api/videos/video-playlists.ts
+++ b/server/tests/api/videos/video-playlists.ts
@@ -61,6 +61,7 @@ import {
61 removeServerFromAccountBlocklist, 61 removeServerFromAccountBlocklist,
62 removeServerFromServerBlocklist 62 removeServerFromServerBlocklist
63} from '../../../../shared/extra-utils/users/blocklist' 63} from '../../../../shared/extra-utils/users/blocklist'
64import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
64 65
65const expect = chai.expect 66const expect = chai.expect
66 67
@@ -1091,7 +1092,7 @@ describe('Test video playlists', function () {
1091 await waitJobs(servers) 1092 await waitJobs(servers)
1092 1093
1093 for (const server of servers) { 1094 for (const server of servers) {
1094 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200) 1095 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1095 } 1096 }
1096 1097
1097 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE } 1098 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
@@ -1100,11 +1101,11 @@ describe('Test video playlists', function () {
1100 await waitJobs(servers) 1101 await waitJobs(servers)
1101 1102
1102 for (const server of [ servers[1], servers[2] ]) { 1103 for (const server of [ servers[1], servers[2] ]) {
1103 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404) 1104 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1104 } 1105 }
1105 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, 401) 1106 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1106 1107
1107 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, 200) 1108 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1108 }) 1109 })
1109 }) 1110 })
1110 1111
@@ -1118,7 +1119,7 @@ describe('Test video playlists', function () {
1118 await waitJobs(servers) 1119 await waitJobs(servers)
1119 1120
1120 for (const server of servers) { 1121 for (const server of servers) {
1121 await getVideoPlaylist(server.url, playlistServer1UUID, 404) 1122 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1122 } 1123 }
1123 }) 1124 })
1124 1125
@@ -1178,7 +1179,7 @@ describe('Test video playlists', function () {
1178 expect(res3.body.displayName).to.equal('channel playlist') 1179 expect(res3.body.displayName).to.equal('channel playlist')
1179 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE) 1180 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1180 1181
1181 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, 404) 1182 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1182 }) 1183 })
1183 1184
1184 it('Should delete an account and delete its playlists', async function () { 1185 it('Should delete an account and delete its playlists', async function () {
diff --git a/server/tests/api/videos/video-privacy.ts b/server/tests/api/videos/video-privacy.ts
index 38e93bbe6..f25d75af4 100644
--- a/server/tests/api/videos/video-privacy.ts
+++ b/server/tests/api/videos/video-privacy.ts
@@ -18,6 +18,7 @@ import { createUser } from '../../../../shared/extra-utils/users/users'
18import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos' 18import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20import { Video } from '@shared/models' 20import { Video } from '@shared/models'
21import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
21 22
22const expect = chai.expect 23const expect = chai.expect
23 24
@@ -110,8 +111,8 @@ describe('Test video privacy', function () {
110 }) 111 })
111 112
112 it('Should not be able to watch the private/internal video with non authenticated user', async function () { 113 it('Should not be able to watch the private/internal video with non authenticated user', async function () {
113 await getVideo(servers[0].url, privateVideoUUID, 401) 114 await getVideo(servers[0].url, privateVideoUUID, HttpStatusCode.UNAUTHORIZED_401)
114 await getVideo(servers[0].url, internalVideoUUID, 401) 115 await getVideo(servers[0].url, internalVideoUUID, HttpStatusCode.UNAUTHORIZED_401)
115 }) 116 })
116 117
117 it('Should not be able to watch the private video with another user', async function () { 118 it('Should not be able to watch the private video with another user', async function () {
@@ -124,15 +125,15 @@ describe('Test video privacy', function () {
124 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 125 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
125 126
126 anotherUserToken = await userLogin(servers[0], user) 127 anotherUserToken = await userLogin(servers[0], user)
127 await getVideoWithToken(servers[0].url, anotherUserToken, privateVideoUUID, 403) 128 await getVideoWithToken(servers[0].url, anotherUserToken, privateVideoUUID, HttpStatusCode.FORBIDDEN_403)
128 }) 129 })
129 130
130 it('Should be able to watch the internal video with another user', async function () { 131 it('Should be able to watch the internal video with another user', async function () {
131 await getVideoWithToken(servers[0].url, anotherUserToken, internalVideoUUID, 200) 132 await getVideoWithToken(servers[0].url, anotherUserToken, internalVideoUUID, HttpStatusCode.OK_200)
132 }) 133 })
133 134
134 it('Should be able to watch the private video with the correct user', async function () { 135 it('Should be able to watch the private video with the correct user', async function () {
135 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, 200) 136 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, HttpStatusCode.OK_200)
136 }) 137 })
137 138
138 it('Should upload an unlisted video on server 2', async function () { 139 it('Should upload an unlisted video on server 2', async function () {
@@ -202,7 +203,7 @@ describe('Test video privacy', function () {
202 }) 203 })
203 204
204 it('Should not be able to get non-federated unlisted video from federated server', async function () { 205 it('Should not be able to get non-federated unlisted video from federated server', async function () {
205 await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, 404) 206 await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, HttpStatusCode.NOT_FOUND_404)
206 }) 207 })
207 208
208 it('Should update the private and internal videos to public on server 1', async function () { 209 it('Should update the private and internal videos to public on server 1', async function () {
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts
index 6f90e9a57..661d603cb 100644
--- a/server/tests/api/videos/videos-history.ts
+++ b/server/tests/api/videos/videos-history.ts
@@ -20,6 +20,7 @@ import {
20} from '../../../../shared/extra-utils' 20} from '../../../../shared/extra-utils'
21import { Video, VideoDetails } from '../../../../shared/models/videos' 21import { Video, VideoDetails } from '../../../../shared/models/videos'
22import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/extra-utils/videos/video-history' 22import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/extra-utils/videos/video-history'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 24
24const expect = chai.expect 25const expect = chai.expect
25 26
@@ -171,7 +172,7 @@ describe('Test videos history', function () {
171 videosHistoryEnabled: false 172 videosHistoryEnabled: false
172 }) 173 })
173 174
174 await userWatchVideo(server.url, server.accessToken, video2UUID, 8, 409) 175 await userWatchVideo(server.url, server.accessToken, video2UUID, 8, HttpStatusCode.CONFLICT_409)
175 }) 176 })
176 177
177 it('Should re-enable videos history', async function () { 178 it('Should re-enable videos history', async function () {
diff --git a/server/tests/cli/reset-password.ts b/server/tests/cli/reset-password.ts
index 6abb6738f..a84463b33 100644
--- a/server/tests/cli/reset-password.ts
+++ b/server/tests/cli/reset-password.ts
@@ -10,6 +10,7 @@ import {
10 ServerInfo, 10 ServerInfo,
11 setAccessTokensToServers 11 setAccessTokensToServers
12} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
13 14
14describe('Test reset password scripts', function () { 15describe('Test reset password scripts', function () {
15 let server: ServerInfo 16 let server: ServerInfo
@@ -28,7 +29,7 @@ describe('Test reset password scripts', function () {
28 const env = getEnvCli(server) 29 const env = getEnvCli(server)
29 await execCLI(`echo coucou | ${env} npm run reset-password -- -u user_1`) 30 await execCLI(`echo coucou | ${env} npm run reset-password -- -u user_1`)
30 31
31 await login(server.url, server.client, { username: 'user_1', password: 'coucou' }, 200) 32 await login(server.url, server.client, { username: 'user_1', password: 'coucou' }, HttpStatusCode.OK_200)
32 }) 33 })
33 34
34 after(async function () { 35 after(async function () {
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 92a468192..f1055ea44 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -31,6 +31,7 @@ import {
31import { waitJobs } from '../../../shared/extra-utils/server/jobs' 31import { waitJobs } from '../../../shared/extra-utils/server/jobs'
32import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments' 32import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
33import { User } from '../../../shared/models/users' 33import { User } from '../../../shared/models/users'
34import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
34 35
35chai.use(require('chai-xml')) 36chai.use(require('chai-xml'))
36chai.use(require('chai-json-schema')) 37chai.use(require('chai-json-schema'))
@@ -330,11 +331,16 @@ describe('Test syndication feeds', () => {
330 }) 331 })
331 332
332 it('Should fail with an invalid token', async function () { 333 it('Should fail with an invalid token', async function () {
333 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, 403) 334 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, HttpStatusCode.FORBIDDEN_403)
334 }) 335 })
335 336
336 it('Should fail with a token of another user', async function () { 337 it('Should fail with a token of another user', async function () {
337 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: userFeedToken }, 403) 338 await getJSONfeed(
339 servers[0].url,
340 'subscriptions',
341 { accountId: feeduserAccountId, token: userFeedToken },
342 HttpStatusCode.FORBIDDEN_403
343 )
338 }) 344 })
339 345
340 it('Should list no videos for a user with videos but no subscriptions', async function () { 346 it('Should list no videos for a user with videos but no subscriptions', async function () {
@@ -382,7 +388,12 @@ describe('Test syndication feeds', () => {
382 it('Should renew the token, and so have an invalid old token', async function () { 388 it('Should renew the token, and so have an invalid old token', async function () {
383 await renewUserScopedTokens(servers[0].url, userAccessToken) 389 await renewUserScopedTokens(servers[0].url, userAccessToken)
384 390
385 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 3 }, 403) 391 await getJSONfeed(
392 servers[0].url,
393 'subscriptions',
394 { accountId: userAccountId, token: userFeedToken, version: 3 },
395 HttpStatusCode.FORBIDDEN_403
396 )
386 }) 397 })
387 398
388 it('Should succeed with the new token', async function () { 399 it('Should succeed with the new token', async function () {
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 2441940c3..cdde6cd30 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -31,6 +31,7 @@ import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../s
31import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' 31import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports'
32import { VideoDetails, VideoImport, VideoImportState, VideoPrivacy } from '../../../shared/models/videos' 32import { VideoDetails, VideoImport, VideoImportState, VideoPrivacy } from '../../../shared/models/videos'
33import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' 33import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
34import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
34 35
35const expect = chai.expect 36const expect = chai.expect
36 37
@@ -127,7 +128,7 @@ describe('Test plugin filter hooks', function () {
127 }) 128 })
128 129
129 it('Should run filter:api.video.upload.accept.result', async function () { 130 it('Should run filter:api.video.upload.accept.result', async function () {
130 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video with bad word' }, 403) 131 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video with bad word' }, HttpStatusCode.FORBIDDEN_403)
131 }) 132 })
132 133
133 it('Should run filter:api.live-video.create.accept.result', async function () { 134 it('Should run filter:api.live-video.create.accept.result', async function () {
@@ -137,7 +138,7 @@ describe('Test plugin filter hooks', function () {
137 channelId: servers[0].videoChannel.id 138 channelId: servers[0].videoChannel.id
138 } 139 }
139 140
140 await createLive(servers[0].url, servers[0].accessToken, attributes, 403) 141 await createLive(servers[0].url, servers[0].accessToken, attributes, HttpStatusCode.FORBIDDEN_403)
141 }) 142 })
142 143
143 it('Should run filter:api.video.pre-import-url.accept.result', async function () { 144 it('Should run filter:api.video.pre-import-url.accept.result', async function () {
@@ -147,7 +148,7 @@ describe('Test plugin filter hooks', function () {
147 channelId: servers[0].videoChannel.id, 148 channelId: servers[0].videoChannel.id,
148 targetUrl: getGoodVideoUrl() + 'bad' 149 targetUrl: getGoodVideoUrl() + 'bad'
149 } 150 }
150 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, 403) 151 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403)
151 }) 152 })
152 153
153 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () { 154 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
@@ -157,7 +158,7 @@ describe('Test plugin filter hooks', function () {
157 channelId: servers[0].videoChannel.id, 158 channelId: servers[0].videoChannel.id,
158 torrentfile: 'video-720p.torrent' as any 159 torrentfile: 'video-720p.torrent' as any
159 } 160 }
160 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, 403) 161 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403)
161 }) 162 })
162 163
163 it('Should run filter:api.video.post-import-url.accept.result', async function () { 164 it('Should run filter:api.video.post-import-url.accept.result', async function () {
@@ -219,15 +220,22 @@ describe('Test plugin filter hooks', function () {
219 }) 220 })
220 221
221 it('Should run filter:api.video-thread.create.accept.result', async function () { 222 it('Should run filter:api.video-thread.create.accept.result', async function () {
222 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment with bad word', 403) 223 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment with bad word', HttpStatusCode.FORBIDDEN_403)
223 }) 224 })
224 225
225 it('Should run filter:api.video-comment-reply.create.accept.result', async function () { 226 it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
226 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 227 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread')
227 threadId = res.body.comment.id 228 threadId = res.body.comment.id
228 229
229 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'comment with bad word', 403) 230 await addVideoCommentReply(
230 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'comment with good word', 200) 231 servers[0].url,
232 servers[0].accessToken,
233 videoUUID,
234 threadId,
235 'comment with bad word',
236 HttpStatusCode.FORBIDDEN_403
237 )
238 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'comment with good word', HttpStatusCode.OK_200)
231 }) 239 })
232 240
233 it('Should run filter:api.video-threads.list.params', async function () { 241 it('Should run filter:api.video-threads.list.params', async function () {
@@ -326,7 +334,7 @@ describe('Test plugin filter hooks', function () {
326 }) 334 })
327 335
328 it('Should not allow a signup', async function () { 336 it('Should not allow a signup', async function () {
329 const res = await registerUser(servers[0].url, 'jma', 'password', 403) 337 const res = await registerUser(servers[0].url, 'jma', 'password', HttpStatusCode.FORBIDDEN_403)
330 338
331 expect(res.body.error).to.equal('No jma') 339 expect(res.body.error).to.equal('No jma')
332 }) 340 })
diff --git a/server/tests/plugins/video-constants.ts b/server/tests/plugins/video-constants.ts
index fec9196e2..5ee41fee1 100644
--- a/server/tests/plugins/video-constants.ts
+++ b/server/tests/plugins/video-constants.ts
@@ -16,6 +16,7 @@ import {
16 uploadVideo 16 uploadVideo
17} from '../../../shared/extra-utils' 17} from '../../../shared/extra-utils'
18import { VideoDetails, VideoPlaylistPrivacy } from '../../../shared/models/videos' 18import { VideoDetails, VideoPlaylistPrivacy } from '../../../shared/models/videos'
19import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
19 20
20const expect = chai.expect 21const expect = chai.expect
21 22
@@ -89,12 +90,17 @@ describe('Test plugin altering video constants', function () {
89 90
90 it('Should not be able to create a video with this privacy', async function () { 91 it('Should not be able to create a video with this privacy', async function () {
91 const attrs = { name: 'video', privacy: 2 } 92 const attrs = { name: 'video', privacy: 2 }
92 await uploadVideo(server.url, server.accessToken, attrs, 400) 93 await uploadVideo(server.url, server.accessToken, attrs, HttpStatusCode.BAD_REQUEST_400)
93 }) 94 })
94 95
95 it('Should not be able to create a video with this privacy', async function () { 96 it('Should not be able to create a video with this privacy', async function () {
96 const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE } 97 const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE }
97 await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attrs, expectedStatus: 400 }) 98 await createVideoPlaylist({
99 url: server.url,
100 token: server.accessToken,
101 playlistAttrs: attrs,
102 expectedStatus: HttpStatusCode.BAD_REQUEST_400
103 })
98 }) 104 })
99 105
100 it('Should be able to upload a video with these values', async function () { 106 it('Should be able to upload a video with these values', async function () {
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts
index 1ab58a438..fe482daf4 100644
--- a/server/tools/peertube-redundancy.ts
+++ b/server/tools/peertube-redundancy.ts
@@ -7,6 +7,7 @@ import * as program from 'commander'
7import { getAdminTokenOrDie, getServerCredentials } from './cli' 7import { getAdminTokenOrDie, getServerCredentials } from './cli'
8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' 8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
9import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy' 9import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
10import validator from 'validator' 11import validator from 'validator'
11import * as CliTable3 from 'cli-table3' 12import * as CliTable3 from 'cli-table3'
12import { URL } from 'url' 13import { URL } from 'url'
@@ -124,9 +125,9 @@ async function addRedundancyCLI (options: { videoId: number }) {
124 125
125 process.exit(0) 126 process.exit(0)
126 } catch (err) { 127 } catch (err) {
127 if (err.message.includes(409)) { 128 if (err.message.includes(HttpStatusCode.CONFLICT_409)) {
128 console.error('This video is already duplicated by your instance.') 129 console.error('This video is already duplicated by your instance.')
129 } else if (err.message.includes(404)) { 130 } else if (err.message.includes(HttpStatusCode.NOT_FOUND_404)) {
130 console.error('This video id does not exist.') 131 console.error('This video id does not exist.')
131 } else { 132 } else {
132 console.error(err) 133 console.error(err)
diff --git a/shared/core-utils/miscs/http-error-codes.ts b/shared/core-utils/miscs/http-error-codes.ts
index 8c8b87ba0..9ac8a6c83 100644
--- a/shared/core-utils/miscs/http-error-codes.ts
+++ b/shared/core-utils/miscs/http-error-codes.ts
@@ -1,6 +1,8 @@
1/** 1/**
2 * Hypertext Transfer Protocol (HTTP) response status codes. 2 * Hypertext Transfer Protocol (HTTP) response status codes.
3 * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} 3 * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes}
4 *
5 * WebDAV and other codes useless with regards to PeerTube are not listed.
4 */ 6 */
5export enum HttpStatusCode { 7export enum HttpStatusCode {
6 8
@@ -48,15 +50,6 @@ export enum HttpStatusCode {
48 ACCEPTED_202 = 202, 50 ACCEPTED_202 = 202,
49 51
50 /** 52 /**
51 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.3.4
52 *
53 * SINCE HTTP/1.1
54 * The server is a transforming proxy that received a 200 OK from its origin,
55 * but is returning a modified version of the origin's response.
56 */
57 NON_AUTHORITATIVE_INFORMATION_203 = 203,
58
59 /**
60 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.3.5 53 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.3.5
61 * 54 *
62 * There is no content to send for this request, but the headers may be useful. 55 * There is no content to send for this request, but the headers may be useful.
@@ -78,18 +71,6 @@ export enum HttpStatusCode {
78 PARTIAL_CONTENT_206 = 206, 71 PARTIAL_CONTENT_206 = 206,
79 72
80 /** 73 /**
81 * The message body that follows is an XML message and can contain a number of separate response codes,
82 * depending on how many sub-requests were made.
83 */
84 MULTI_STATUS_207 = 207,
85
86 /**
87 * The server has fulfilled a request for the resource,
88 * and the response is a representation of the result of one or more instance-manipulations applied to the current instance.
89 */
90 IM_USED_226 = 226,
91
92 /**
93 * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). 74 * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation).
94 * For example, this code could be used to present multiple video format options, 75 * For example, this code could be used to present multiple video format options,
95 * to list files with different filename extensions, or to suggest word-sense disambiguation. 76 * to list files with different filename extensions, or to suggest word-sense disambiguation.
@@ -129,20 +110,6 @@ export enum HttpStatusCode {
129 NOT_MODIFIED_304 = 304, 110 NOT_MODIFIED_304 = 304,
130 111
131 /** 112 /**
132 * @deprecated
133 * SINCE HTTP/1.1
134 * The requested resource is available only through a proxy, the address for which is provided in the response.
135 * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status
136 * code, primarily for security reasons.
137 */
138 USE_PROXY_305 = 305,
139
140 /**
141 * No longer used. Originally meant "Subsequent requests should use the specified proxy."
142 */
143 SWITCH_PROXY_306 = 306,
144
145 /**
146 * SINCE HTTP/1.1 113 * SINCE HTTP/1.1
147 * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. 114 * In this case, the request should be repeated with another URI; however, future requests should still use the original URI.
148 * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the 115 * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the
@@ -175,6 +142,8 @@ export enum HttpStatusCode {
175 UNAUTHORIZED_401 = 401, 142 UNAUTHORIZED_401 = 401,
176 143
177 /** 144 /**
145 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.5.2
146 *
178 * Reserved for future use. The original intention was that this code might be used as part of some form of digital 147 * Reserved for future use. The original intention was that this code might be used as part of some form of digital
179 * cash or micro payment scheme, but that has not happened, and this code is not usually used. 148 * cash or micro payment scheme, but that has not happened, and this code is not usually used.
180 * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. 149 * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests.
@@ -211,21 +180,20 @@ export enum HttpStatusCode {
211 NOT_ACCEPTABLE_406 = 406, 180 NOT_ACCEPTABLE_406 = 406,
212 181
213 /** 182 /**
214 * The client must first authenticate itself with the proxy.
215 */
216 PROXY_AUTHENTICATION_REQUIRED_407 = 407,
217
218 /**
219 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.5.7 183 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.5.7
220 * 184 *
221 * This response is sent on an idle connection by some servers, even without any previous request by the client. 185 * This response is sent on an idle connection by some servers, even without any previous request by the client.
222 * It means that the server would like to shut down this unused connection. This response is used much more since 186 * It means that the server would like to shut down this unused connection. This response is used much more since
223 * some browsers, like Chrome, Firefox 27+, or IE9, use HTTP pre-connection mechanisms to speed up surfing. Also 187 * some browsers, like Chrome, Firefox 27+, or IE9, use HTTP pre-connection mechanisms to speed up surfing. Also
224 * note that some servers merely shut down the connection without sending this message. 188 * note that some servers merely shut down the connection without sending this message.
189 *
190 * @
225 */ 191 */
226 REQUEST_TIMEOUT_408 = 408, 192 REQUEST_TIMEOUT_408 = 408,
227 193
228 /** 194 /**
195 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.5.8
196 *
229 * Indicates that the request could not be processed because of conflict in the request, 197 * Indicates that the request could not be processed because of conflict in the request,
230 * such as an edit conflict between multiple simultaneous updates. 198 * such as an edit conflict between multiple simultaneous updates.
231 */ 199 */
@@ -284,11 +252,13 @@ export enum HttpStatusCode {
284 RANGE_NOT_SATISFIABLE_416 = 416, 252 RANGE_NOT_SATISFIABLE_416 = 416,
285 253
286 /** 254 /**
287 * The server cannot meet the requirements of the Expect request-header field. 255 * The server cannot meet the requirements of the `Expect` request-header field.
288 */ 256 */
289 EXPECTATION_FAILED_417 = 417, 257 EXPECTATION_FAILED_417 = 417,
290 258
291 /** 259 /**
260 * Official Documentation @ https://tools.ietf.org/html/rfc2324
261 *
292 * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, 262 * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol,
293 * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by 263 * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by
294 * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including PeerTube instances ;-). 264 * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including PeerTube instances ;-).
@@ -296,41 +266,26 @@ export enum HttpStatusCode {
296 I_AM_A_TEAPOT_418 = 418, 266 I_AM_A_TEAPOT_418 = 418,
297 267
298 /** 268 /**
299 * The request was directed at a server that is not able to produce a response (for example because a connection reuse).
300 */
301 MISDIRECTED_REQUEST_421 = 421,
302
303 /**
304 * Official Documentation @ https://tools.ietf.org/html/rfc2518#section-10.3 269 * Official Documentation @ https://tools.ietf.org/html/rfc2518#section-10.3
305 * 270 *
306 * The request was well-formed but was unable to be followed due to semantic errors. 271 * The request was well-formed but was unable to be followed due to semantic errors.
272 *
273 * @see HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415 if the `Content-Type` was not supported.
274 * @see HttpStatusCode.BAD_REQUEST_400 if the request was not parsable (broken JSON, XML)
307 */ 275 */
308 UNPROCESSABLE_ENTITY_422 = 422, 276 UNPROCESSABLE_ENTITY_422 = 422,
309 277
310 /** 278 /**
311 * The resource that is being accessed is locked. 279 * Official Documentation @ https://tools.ietf.org/html/rfc4918#section-11.3
280 *
281 * The resource that is being accessed is locked. WebDAV-specific but used by some HTTP services.
282 *
283 * @deprecated use `If-Match` / `If-None-Match` instead
284 * @see {@link https://evertpot.com/http/423-locked}
312 */ 285 */
313 LOCKED_423 = 423, 286 LOCKED_423 = 423,
314 287
315 /** 288 /**
316 * The request failed due to failure of a previous request (e.g., a PROPPATCH).
317 */
318 FAILED_DEPENDENCY_424 = 424,
319
320 /**
321 * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.
322 */
323 UPGRADE_REQUIRED_426 = 426,
324
325 /**
326 * The origin server requires the request to be conditional.
327 * Intended to prevent "the 'lost update' problem, where a client
328 * GETs a resource's state, modifies it, and PUTs it back to the server,
329 * when meanwhile a third party has modified the state on the server, leading to a conflict."
330 */
331 PRECONDITION_REQUIRED_428 = 428,
332
333 /**
334 * Official Documentation @ https://tools.ietf.org/html/rfc6585#section-4 289 * Official Documentation @ https://tools.ietf.org/html/rfc6585#section-4
335 * 290 *
336 * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. 291 * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes.
@@ -359,6 +314,8 @@ export enum HttpStatusCode {
359 INTERNAL_SERVER_ERROR_500 = 500, 314 INTERNAL_SERVER_ERROR_500 = 500,
360 315
361 /** 316 /**
317 * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.6.2
318 *
362 * The server either does not recognize the request method, or it lacks the ability to fulfill the request. 319 * The server either does not recognize the request method, or it lacks the ability to fulfill the request.
363 * Usually this implies future availability (e.g., a new feature of a web-service API). 320 * Usually this implies future availability (e.g., a new feature of a web-service API).
364 */ 321 */
@@ -386,34 +343,14 @@ export enum HttpStatusCode {
386 HTTP_VERSION_NOT_SUPPORTED_505 = 505, 343 HTTP_VERSION_NOT_SUPPORTED_505 = 505,
387 344
388 /** 345 /**
389 * Transparent content negotiation for the request results in a circular reference.
390 */
391 VARIANT_ALSO_NEGOTIATES_506 = 506,
392
393 /**
394 * Official Documentation @ https://tools.ietf.org/html/rfc2518#section-10.6 346 * Official Documentation @ https://tools.ietf.org/html/rfc2518#section-10.6
395 * 347 *
396 * The 507 (Insufficient Storage) status code means the method could not be performed on the resource because the 348 * The 507 (Insufficient Storage) status code means the method could not be performed on the resource because the
397 * server is unable to store the representation needed to successfully complete the request. This condition is 349 * server is unable to store the representation needed to successfully complete the request. This condition is
398 * considered to be temporary. If the request which received this status code was the result of a user action, 350 * considered to be temporary. If the request which received this status code was the result of a user action,
399 * the request MUST NOT be repeated until it is requested by a separate user action. 351 * the request MUST NOT be repeated until it is requested by a separate user action.
352 *
353 * @see HttpStatusCode.PAYLOAD_TOO_LARGE_413 for quota errors
400 */ 354 */
401 INSUFFICIENT_STORAGE_507 = 507, 355 INSUFFICIENT_STORAGE_507 = 507,
402
403 /**
404 * The server detected an infinite loop while processing the request.
405 */
406 LOOP_DETECTED_508 = 508,
407
408 /**
409 * Further extensions to the request are required for the server to fulfill it.
410 */
411 NOT_EXTENDED_510 = 510,
412
413 /**
414 * The client needs to authenticate to gain network access.
415 * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used
416 * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot).
417 */
418 NETWORK_AUTHENTICATION_REQUIRED_511 = 511
419} 356}
diff --git a/shared/extra-utils/server/activitypub.ts b/shared/extra-utils/server/activitypub.ts
index eccb198ca..cf967ed7d 100644
--- a/shared/extra-utils/server/activitypub.ts
+++ b/shared/extra-utils/server/activitypub.ts
@@ -1,6 +1,7 @@
1import * as request from 'supertest' 1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
2 3
3function makeActivityPubGetRequest (url: string, path: string, expectedStatus = 200) { 4function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
4 return request(url) 5 return request(url)
5 .get(path) 6 .get(path)
6 .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8') 7 .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
diff --git a/shared/extra-utils/server/redundancy.ts b/shared/extra-utils/server/redundancy.ts
index 3aca4ebfd..b83815a37 100644
--- a/shared/extra-utils/server/redundancy.ts
+++ b/shared/extra-utils/server/redundancy.ts
@@ -2,7 +2,13 @@ import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequ
2import { VideoRedundanciesTarget } from '@shared/models' 2import { VideoRedundanciesTarget } from '@shared/models'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4 4
5function updateRedundancy (url: string, accessToken: string, host: string, redundancyAllowed: boolean, expectedStatus = 204) { 5function updateRedundancy (
6 url: string,
7 accessToken: string,
8 host: string,
9 redundancyAllowed: boolean,
10 expectedStatus = HttpStatusCode.NO_CONTENT_204
11) {
6 const path = '/api/v1/server/redundancy/' + host 12 const path = '/api/v1/server/redundancy/' + host
7 13
8 return makePutBodyRequest({ 14 return makePutBodyRequest({
diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts
index 275bb0826..39e1a2747 100644
--- a/shared/extra-utils/users/login.ts
+++ b/shared/extra-utils/users/login.ts
@@ -2,12 +2,13 @@ import * as request from 'supertest'
2 2
3import { ServerInfo } from '../server/servers' 3import { ServerInfo } from '../server/servers'
4import { getClient } from '../server/clients' 4import { getClient } from '../server/clients'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5 6
6type Client = { id: string, secret: string } 7type Client = { id: string, secret: string }
7type User = { username: string, password: string } 8type User = { username: string, password: string }
8type Server = { url: string, client: Client, user: User } 9type Server = { url: string, client: Client, user: User }
9 10
10function login (url: string, client: Client, user: User, expectedStatus = 200) { 11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) {
11 const path = '/api/v1/users/token' 12 const path = '/api/v1/users/token'
12 13
13 const body = { 14 const body = {
@@ -27,7 +28,7 @@ function login (url: string, client: Client, user: User, expectedStatus = 200) {
27 .expect(expectedStatus) 28 .expect(expectedStatus)
28} 29}
29 30
30function logout (url: string, token: string, expectedStatus = 200) { 31function logout (url: string, token: string, expectedStatus = HttpStatusCode.OK_200) {
31 const path = '/api/v1/users/revoke-token' 32 const path = '/api/v1/users/revoke-token'
32 33
33 return request(url) 34 return request(url)
@@ -38,12 +39,12 @@ function logout (url: string, token: string, expectedStatus = 200) {
38} 39}
39 40
40async function serverLogin (server: Server) { 41async function serverLogin (server: Server) {
41 const res = await login(server.url, server.client, server.user, 200) 42 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200)
42 43
43 return res.body.access_token as string 44 return res.body.access_token as string
44} 45}
45 46
46function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = 200) { 47function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = HttpStatusCode.OK_200) {
47 const path = '/api/v1/users/token' 48 const path = '/api/v1/users/token'
48 49
49 const body = { 50 const body = {
@@ -61,7 +62,7 @@ function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus
61 .expect(expectedStatus) 62 .expect(expectedStatus)
62} 63}
63 64
64async function userLogin (server: Server, user: User, expectedStatus = 200) { 65async function userLogin (server: Server, user: User, expectedStatus = HttpStatusCode.OK_200) {
65 const res = await login(server.url, server.client, user, expectedStatus) 66 const res = await login(server.url, server.client, user, expectedStatus)
66 67
67 return res.body.access_token as string 68 return res.body.access_token as string
@@ -95,7 +96,7 @@ function setAccessTokensToServers (servers: ServerInfo[]) {
95 return Promise.all(tasks) 96 return Promise.all(tasks)
96} 97}
97 98
98function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = 200) { 99function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = HttpStatusCode.OK_200) {
99 const path = '/api/v1/users/token' 100 const path = '/api/v1/users/token'
100 101
101 const body = { 102 const body = {
diff --git a/shared/extra-utils/videos/video-history.ts b/shared/extra-utils/videos/video-history.ts
index 2d751cf14..0dd3afb24 100644
--- a/shared/extra-utils/videos/video-history.ts
+++ b/shared/extra-utils/videos/video-history.ts
@@ -1,7 +1,13 @@
1import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' 1import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3 3
4function userWatchVideo (url: string, token: string, videoId: number | string, currentTime: number, statusCodeExpected = 204) { 4function userWatchVideo (
5 url: string,
6 token: string,
7 videoId: number | string,
8 currentTime: number,
9 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
10) {
5 const path = '/api/v1/videos/' + videoId + '/watching' 11 const path = '/api/v1/videos/' + videoId + '/watching'
6 const fields = { currentTime } 12 const fields = { currentTime }
7 13
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index a4b9d688e..a2438d712 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -155,7 +155,7 @@ function getVideosListWithToken (url: string, token: string, query: { nsfw?: boo
155 .set('Authorization', 'Bearer ' + token) 155 .set('Authorization', 'Bearer ' + token)
156 .query(immutableAssign(query, { sort: 'name' })) 156 .query(immutableAssign(query, { sort: 'name' }))
157 .set('Accept', 'application/json') 157 .set('Accept', 'application/json')
158 .expect(200) 158 .expect(HttpStatusCode.OK_200)
159 .expect('Content-Type', /json/) 159 .expect('Content-Type', /json/)
160} 160}
161 161
@@ -166,7 +166,7 @@ function getLocalVideos (url: string) {
166 .get(path) 166 .get(path)
167 .query({ sort: 'name', filter: 'local' }) 167 .query({ sort: 'name', filter: 'local' })
168 .set('Accept', 'application/json') 168 .set('Accept', 'application/json')
169 .expect(200) 169 .expect(HttpStatusCode.OK_200)
170 .expect('Content-Type', /json/) 170 .expect('Content-Type', /json/)
171} 171}
172 172
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 2d6b4df27..ba420b4a9 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -1312,20 +1312,24 @@ paths:
1312 application/json: 1312 application/json:
1313 schema: 1313 schema:
1314 $ref: '#/components/schemas/VideoUploadResponse' 1314 $ref: '#/components/schemas/VideoUploadResponse'
1315 '400':
1316 description: invalid file field, schedule date or parameter
1315 '403': 1317 '403':
1316 description: user video quota is exceeded with this video 1318 description: video didn't pass upload filter
1317 '408': 1319 '408':
1318 description: upload has timed out 1320 description: upload has timed out
1319 '413': 1321 '413':
1320 description: video file too large 1322 description: video file too large, due to quota or max body size limit set by the reverse-proxy
1321 headers: 1323 headers:
1322 X-File-Maximum-Size: 1324 X-File-Maximum-Size:
1323 schema: 1325 schema:
1324 type: string 1326 type: string
1325 format: Nginx size 1327 format: Nginx size
1326 description: Maximum file size for the video 1328 description: Maximum file size for the video
1329 '415':
1330 description: video type unsupported
1327 '422': 1331 '422':
1328 description: invalid input file 1332 description: video unreadable
1329 requestBody: 1333 requestBody:
1330 content: 1334 content:
1331 multipart/form-data: 1335 multipart/form-data:
@@ -1534,10 +1538,12 @@ paths:
1534 application/json: 1538 application/json:
1535 schema: 1539 schema:
1536 $ref: '#/components/schemas/VideoUploadResponse' 1540 $ref: '#/components/schemas/VideoUploadResponse'
1537 '409':
1538 description: HTTP or Torrent/magnetURI import not enabled
1539 '400': 1541 '400':
1540 description: '`magnetUri` or `targetUrl` or a torrent file missing' 1542 description: '`magnetUri` or `targetUrl` or a torrent file missing'
1543 '403':
1544 description: video didn't pass pre-import filter
1545 '409':
1546 description: HTTP or Torrent/magnetURI import not enabled
1541 1547
1542 /videos/live: 1548 /videos/live:
1543 post: 1549 post:
@@ -3572,7 +3578,7 @@ components:
3572 name: name 3578 name: name
3573 in: path 3579 in: path
3574 required: true 3580 required: true
3575 description: The name of the account 3581 description: The username or handle of the account
3576 schema: 3582 schema:
3577 type: string 3583 type: string
3578 example: chocobozzz | chocobozzz@example.org 3584 example: chocobozzz | chocobozzz@example.org
@@ -5175,7 +5181,7 @@ components:
5175 properties: 5181 properties:
5176 name: 5182 name:
5177 type: string 5183 type: string
5178 description: The name for the default channel 5184 description: The username for the default channel
5179 pattern: '/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]+$/' 5185 pattern: '/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]+$/'
5180 displayName: 5186 displayName:
5181 type: string 5187 type: string