diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts index 0a52985da..24bde61ce 100644 --- a/client/src/app/+accounts/accounts.component.ts +++ b/client/src/app/+accounts/accounts.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { AccountService } from '@app/shared/account/account.service' import { Account } from '@app/shared/account/account.model' +import { RestExtractor } from '@app/shared' +import { catchError } from 'rxjs/operators' @Component({ templateUrl: './accounts.component.html', @@ -12,13 +14,15 @@ export class AccountsComponent implements OnInit { constructor ( private route: ActivatedRoute, - private accountService: AccountService + private accountService: AccountService, + private restExtractor: RestExtractor ) {} ngOnInit () { const accountId = this.route.snapshot.params['accountId'] this.accountService.getAccount(accountId) + .pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))) .subscribe(account => this.account = account) } } diff --git a/client/src/app/+page-not-found/page-not-found-routing.module.ts b/client/src/app/+page-not-found/page-not-found-routing.module.ts new file mode 100644 index 000000000..43f7d7337 --- /dev/null +++ b/client/src/app/+page-not-found/page-not-found-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core' +import { RouterModule, Routes } from '@angular/router' +import { PageNotFoundComponent } from './page-not-found.component' + +const pageNotFoundRoutes: Routes = [ + { + path: '', + component: PageNotFoundComponent, + } +] + +@NgModule({ + imports: [ RouterModule.forChild(pageNotFoundRoutes) ], + exports: [ RouterModule ] +}) +export class PageNotFoundRoutingModule {} diff --git a/client/src/app/+page-not-found/page-not-found.component.html b/client/src/app/+page-not-found/page-not-found.component.html new file mode 100644 index 000000000..66aa2aec7 --- /dev/null +++ b/client/src/app/+page-not-found/page-not-found.component.html @@ -0,0 +1,3 @@ +
+ Sorry, but we couldn't find the page you were looking for. +
\ No newline at end of file diff --git a/client/src/app/+page-not-found/page-not-found.component.scss b/client/src/app/+page-not-found/page-not-found.component.scss new file mode 100644 index 000000000..05d45f97f --- /dev/null +++ b/client/src/app/+page-not-found/page-not-found.component.scss @@ -0,0 +1,8 @@ +div { + height: 100%; + width: 100%; + text-align: center; + margin-top: 50px; + + font-size: 32px; +} \ No newline at end of file diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts new file mode 100644 index 000000000..c91bb8649 --- /dev/null +++ b/client/src/app/+page-not-found/page-not-found.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core' + +@Component({ + selector: 'my-page-not-found', + templateUrl: './page-not-found.component.html', + styleUrls: [ './page-not-found.component.scss' ] +}) +export class PageNotFoundComponent { + +} diff --git a/client/src/app/+page-not-found/page-not-found.module.ts b/client/src/app/+page-not-found/page-not-found.module.ts new file mode 100644 index 000000000..bc29d17c4 --- /dev/null +++ b/client/src/app/+page-not-found/page-not-found.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core' +import { SharedModule } from '../shared' +import { PageNotFoundComponent } from '@app/+page-not-found/page-not-found.component' +import { PageNotFoundRoutingModule } from '@app/+page-not-found/page-not-found-routing.module' + +@NgModule({ + imports: [ + PageNotFoundRoutingModule, + SharedModule + ], + + declarations: [ + PageNotFoundComponent, + ], + + exports: [ + PageNotFoundComponent + ], + + providers: [] +}) +export class PageNotFoundModule { } diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts index 5eca64fb5..09541b370 100644 --- a/client/src/app/+video-channels/video-channels.component.ts +++ b/client/src/app/+video-channels/video-channels.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' +import { RestExtractor } from '@app/shared' +import { catchError } from 'rxjs/operators' @Component({ templateUrl: './video-channels.component.html', @@ -12,13 +14,15 @@ export class VideoChannelsComponent implements OnInit { constructor ( private route: ActivatedRoute, - private videoChannelService: VideoChannelService + private videoChannelService: VideoChannelService, + private restExtractor: RestExtractor ) {} ngOnInit () { const videoChannelId = this.route.snapshot.params['videoChannelId'] this.videoChannelService.getVideoChannel(videoChannelId) + .pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))) .subscribe(videoChannel => this.videoChannel = videoChannel) } } diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 936912d28..04c53548e 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -19,6 +19,10 @@ const routes: Routes = [ { path: 'video-channels', loadChildren: './+video-channels/video-channels.module#VideoChannelsModule' + }, + { + path: '**', + loadChildren: './+page-not-found/page-not-found.module#PageNotFoundModule' } ] diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 874fc43aa..0b4144e39 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -45,8 +45,6 @@ export function metaFactory (serverService: ServerService): MetaLoader { CoreModule, SharedModule, - AppRoutingModule, - CoreModule, LoginModule, ResetPasswordModule, @@ -59,7 +57,9 @@ export function metaFactory (serverService: ServerService): MetaLoader { provide: MetaLoader, useFactory: (metaFactory), deps: [ ServerService ] - }) + }), + + AppRoutingModule, // Put it after all the module because it has the 404 route ], providers: [ ] }) diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index abe044d73..844f184b4 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts @@ -19,30 +19,29 @@ export class RedirectService { } this.serverService.configLoaded - .subscribe(() => { - const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute + .subscribe(() => { + const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute - if (defaultRouteConfig) { - RedirectService.DEFAULT_ROUTE = defaultRouteConfig - } - }) + if (defaultRouteConfig) { + RedirectService.DEFAULT_ROUTE = defaultRouteConfig + } + }) } redirectToHomepage () { console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE) this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { replaceUrl: true }) - .catch(() => { - console.error( - 'Cannot navigate to %s, resetting default route to %s.', - RedirectService.DEFAULT_ROUTE, - RedirectService.INIT_DEFAULT_ROUTE - ) + .catch(() => { + console.error( + 'Cannot navigate to %s, resetting default route to %s.', + RedirectService.DEFAULT_ROUTE, + RedirectService.INIT_DEFAULT_ROUTE + ) - RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE - return this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { replaceUrl: true }) - }) + RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE + return this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { replaceUrl: true }) + }) } - } diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts index db087d407..39e601e20 100644 --- a/client/src/app/shared/rest/rest-extractor.service.ts +++ b/client/src/app/shared/rest/rest-extractor.service.ts @@ -1,11 +1,16 @@ -import { of, throwError as observableThrowError } from 'rxjs' +import { throwError as observableThrowError } from 'rxjs' import { Injectable } from '@angular/core' import { dateToHuman } from '@app/shared/misc/utils' import { ResultList } from '../../../../../shared' +import { Router } from '@angular/router' @Injectable() export class RestExtractor { + constructor (private router: Router) { + // empty + } + extractDataBool () { return true } @@ -87,4 +92,13 @@ export class RestExtractor { return observableThrowError(errorObj) } + + redirectTo404IfNotFound (obj: { status: number }, status = [ 404 ]) { + if (obj && obj.status && status.indexOf(obj.status) !== -1) { + // Do not use redirectService to avoid circular dependencies + this.router.navigate([ '/404' ], { skipLocationChange: true }) + } + + return observableThrowError(obj) + } } 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 c71051649..ad572ef58 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -1,3 +1,4 @@ +import { catchError } from 'rxjs/operators' import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { RedirectService } from '@app/core/routing/redirect.service' @@ -12,7 +13,7 @@ import * as WebTorrent from 'webtorrent' import { UserVideoRateType, VideoRateType } from '../../../../../shared' import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' -import { VideoBlacklistService } from '../../shared' +import { RestExtractor, VideoBlacklistService } from '../../shared' import { VideoDetails } from '../../shared/video/video-details.model' import { Video } from '../../shared/video/video.model' import { VideoService } from '../../shared/video/video.service' @@ -65,6 +66,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private metaService: MetaService, private authService: AuthService, private serverService: ServerService, + private restExtractor: RestExtractor, private notificationsService: NotificationsService, private markdownService: MarkdownService, private zone: NgZone, @@ -99,21 +101,25 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } const uuid = routeParams['uuid'] + // Video did not change if (this.video && this.video.uuid === uuid) return // Video did change - this.videoService.getVideo(uuid).subscribe( - video => { - const startTime = this.route.snapshot.queryParams.start - this.onVideoFetched(video, startTime) - .catch(err => this.handleError(err)) - }, + this.videoService + .getVideo(uuid) + .pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))) + .subscribe( + video => { + const startTime = this.route.snapshot.queryParams.start + this.onVideoFetched(video, startTime) + .catch(err => this.handleError(err)) + }, - error => { - this.videoNotFound = true - console.error(error) - } - ) + error => { + this.videoNotFound = true + console.error(error) + } + ) }) } diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 572f33d5e..66153e033 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -1,5 +1,5 @@ import { NgModule } from '@angular/core' -import { RouterModule, Routes } from '@angular/router' +import { RouterModule, Routes, UrlSegment } from '@angular/router' import { VideoLocalComponent } from '@app/videos/video-list/video-local.component' import { MetaGuard } from '@ngx-meta/core' import { VideoSearchComponent } from './video-list' @@ -72,11 +72,6 @@ const videosRoutes: Routes = [ } } }, - { - path: ':uuid', - pathMatch: 'full', - redirectTo: 'watch/:uuid' - }, { path: 'watch/:uuid', loadChildren: 'app/videos/+video-watch/video-watch.module#VideoWatchModule',