redirect to login on 401, display error variants in 404 component
This commit is contained in:
parent
6939cbac48
commit
ab398a05e9
|
@ -50,7 +50,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
||||||
switchMap(accountId => this.accountService.getAccount(accountId)),
|
switchMap(accountId => this.accountService.getAccount(accountId)),
|
||||||
tap(account => this.onAccount(account)),
|
tap(account => this.onAccount(account)),
|
||||||
switchMap(account => this.videoChannelService.listAccountVideoChannels(account)),
|
switchMap(account => this.videoChannelService.listAccountVideoChannels(account)),
|
||||||
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
|
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [
|
||||||
HttpStatusCode.BAD_REQUEST_400,
|
HttpStatusCode.BAD_REQUEST_400,
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -1,23 +1,32 @@
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div *ngIf="status === 404" class="box">
|
<div *ngIf="status !== 403 && status !== 418" class="box">
|
||||||
<strong>{{ status }}.</strong>
|
<strong>{{ status }}.</strong>
|
||||||
<span class="ml-1 text-muted" i18n>That's an error.</span>
|
<span class="ml-1 text-muted" i18n>That's an error.</span>
|
||||||
|
|
||||||
<div class="text mt-4" i18n>
|
<div class="text mt-4" i18n>
|
||||||
We couldn't find any ressource tied to the URL {{ pathname }} you were looking for.
|
We couldn't find any {{ getRessourceName() }} tied to the URL {{ pathname }} you were looking for.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-muted mt-4">
|
<div class="text-muted mt-4">
|
||||||
<span i18n="Possible reasons preceding a list of reasons a `Not Found` error page may occur">Possible reasons:</span>
|
<span i18n="Possible reasons preceding a list of reasons a `Not Found` error page may occur">Possible reasons:</span>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li i18n>The page may have been moved or deleted</li>
|
|
||||||
<li i18n>You may have used an outdated or broken link</li>
|
<li i18n>You may have used an outdated or broken link</li>
|
||||||
|
<li i18n>The {{ getRessourceName() }} may have been moved or deleted</li>
|
||||||
<li i18n>You may have typed the address or URL incorrectly</li>
|
<li i18n>You may have typed the address or URL incorrectly</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="status === 403" class="box">
|
||||||
|
<strong>{{ status }}.</strong>
|
||||||
|
<span class="ml-1 text-muted" i18n>You are not authorized here.</span>
|
||||||
|
|
||||||
|
<div class="text mt-4" i18n>
|
||||||
|
You might need to check your account is allowed by the {{ getRessourceName() }} or instance owner.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="status === 418" class="box">
|
<div *ngIf="status === 418" class="box">
|
||||||
<strong>{{ status }}.</strong>
|
<strong>{{ status }}.</strong>
|
||||||
<span class="ml-1 text-muted">I'm a teapot.</span>
|
<span class="ml-1 text-muted">I'm a teapot.</span>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { Title } from '@angular/platform-browser'
|
import { Title } from '@angular/platform-browser'
|
||||||
|
import { Router } from '@angular/router'
|
||||||
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -9,10 +10,16 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
||||||
})
|
})
|
||||||
export class PageNotFoundComponent implements OnInit {
|
export class PageNotFoundComponent implements OnInit {
|
||||||
status = HttpStatusCode.NOT_FOUND_404
|
status = HttpStatusCode.NOT_FOUND_404
|
||||||
|
type: string
|
||||||
|
|
||||||
public constructor (
|
public constructor (
|
||||||
private titleService: Title
|
private titleService: Title,
|
||||||
) {}
|
private router: Router
|
||||||
|
) {
|
||||||
|
const state = this.router.getCurrentNavigation()?.extras.state
|
||||||
|
this.type = state?.type || this.type
|
||||||
|
this.status = state?.obj.status || this.status
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
if (this.pathname.includes('teapot')) {
|
if (this.pathname.includes('teapot')) {
|
||||||
|
@ -25,10 +32,21 @@ export class PageNotFoundComponent implements OnInit {
|
||||||
return window.location.pathname
|
return window.location.pathname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRessourceName () {
|
||||||
|
switch (this.type) {
|
||||||
|
case 'video':
|
||||||
|
return $localize`video`
|
||||||
|
default:
|
||||||
|
return $localize`ressource`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getMascotName () {
|
getMascotName () {
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case HttpStatusCode.I_AM_A_TEAPOT_418:
|
case HttpStatusCode.I_AM_A_TEAPOT_418:
|
||||||
return 'happy'
|
return 'happy'
|
||||||
|
case HttpStatusCode.FORBIDDEN_403:
|
||||||
|
return 'arguing'
|
||||||
case HttpStatusCode.NOT_FOUND_404:
|
case HttpStatusCode.NOT_FOUND_404:
|
||||||
default:
|
default:
|
||||||
return 'defeated'
|
return 'defeated'
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
||||||
map(params => params[ 'videoChannelName' ]),
|
map(params => params[ 'videoChannelName' ]),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
|
switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
|
||||||
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
|
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [
|
||||||
HttpStatusCode.BAD_REQUEST_400,
|
HttpStatusCode.BAD_REQUEST_400,
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -404,7 +404,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.videoCaptionService.listCaptions(videoId)
|
this.videoCaptionService.listCaptions(videoId)
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
// If 401, the video is private or blocked so redirect to 404
|
// If 400, 403 or 404, the video is private or blocked so redirect to 404
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
if (err.body.errorCode === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) {
|
if (err.body.errorCode === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) {
|
||||||
const search = window.location.search
|
const search = window.location.search
|
||||||
|
@ -416,9 +416,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
$localize`Redirection`
|
$localize`Redirection`
|
||||||
).then(res => {
|
).then(res => {
|
||||||
if (res === false) {
|
if (res === false) {
|
||||||
return this.restExtractor.redirectTo404IfNotFound(err, [
|
return this.restExtractor.redirectTo404IfNotFound(err, 'video', [
|
||||||
HttpStatusCode.BAD_REQUEST_400,
|
HttpStatusCode.BAD_REQUEST_400,
|
||||||
HttpStatusCode.UNAUTHORIZED_401,
|
|
||||||
HttpStatusCode.FORBIDDEN_403,
|
HttpStatusCode.FORBIDDEN_403,
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
])
|
])
|
||||||
|
@ -428,9 +427,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.restExtractor.redirectTo404IfNotFound(err, [
|
return this.restExtractor.redirectTo404IfNotFound(err, 'video', [
|
||||||
HttpStatusCode.BAD_REQUEST_400,
|
HttpStatusCode.BAD_REQUEST_400,
|
||||||
HttpStatusCode.UNAUTHORIZED_401,
|
|
||||||
HttpStatusCode.FORBIDDEN_403,
|
HttpStatusCode.FORBIDDEN_403,
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
])
|
])
|
||||||
|
@ -464,10 +462,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.playlistService.getVideoPlaylist(playlistId)
|
this.playlistService.getVideoPlaylist(playlistId)
|
||||||
.pipe(
|
.pipe(
|
||||||
// If 401, the video is private or blocked so redirect to 404
|
// If 400 or 403, the video is private or blocked so redirect to 404
|
||||||
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [
|
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'video', [
|
||||||
HttpStatusCode.BAD_REQUEST_400,
|
HttpStatusCode.BAD_REQUEST_400,
|
||||||
HttpStatusCode.UNAUTHORIZED_401,
|
|
||||||
HttpStatusCode.FORBIDDEN_403,
|
HttpStatusCode.FORBIDDEN_403,
|
||||||
HttpStatusCode.NOT_FOUND_404
|
HttpStatusCode.NOT_FOUND_404
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -93,10 +93,10 @@ export class RestExtractor {
|
||||||
return observableThrowError(errorObj)
|
return observableThrowError(errorObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectTo404IfNotFound (obj: { status: number }, status = [ HttpStatusCode.NOT_FOUND_404 ]) {
|
redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) {
|
||||||
if (obj && obj.status && status.indexOf(obj.status) !== -1) {
|
if (obj && obj.status && status.indexOf(obj.status) !== -1) {
|
||||||
// Do not use redirectService to avoid circular dependencies
|
// Do not use redirectService to avoid circular dependencies
|
||||||
this.router.navigate([ '/404' ], { skipLocationChange: true })
|
this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
return observableThrowError(obj)
|
return observableThrowError(obj)
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { Observable, throwError as observableThrowError } from 'rxjs'
|
import { Observable, of, throwError as observableThrowError } from 'rxjs'
|
||||||
import { catchError, switchMap } from 'rxjs/operators'
|
import { catchError, switchMap } from 'rxjs/operators'
|
||||||
import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'
|
import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http'
|
||||||
import { Injectable, Injector } from '@angular/core'
|
import { Injectable, Injector } from '@angular/core'
|
||||||
import { AuthService } from '@app/core/auth/auth.service'
|
import { AuthService } from '@app/core/auth/auth.service'
|
||||||
|
import { Router } from '@angular/router'
|
||||||
|
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthInterceptor implements HttpInterceptor {
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
private authService: AuthService
|
private authService: AuthService
|
||||||
|
|
||||||
// https://github.com/angular/angular/issues/18224#issuecomment-316957213
|
// https://github.com/angular/angular/issues/18224#issuecomment-316957213
|
||||||
constructor (private injector: Injector) {}
|
constructor (private injector: Injector, private router: Router) {}
|
||||||
|
|
||||||
intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
if (this.authService === undefined) {
|
if (this.authService === undefined) {
|
||||||
|
@ -22,9 +24,11 @@ export class AuthInterceptor implements HttpInterceptor {
|
||||||
// Catch 401 errors (refresh token expired)
|
// Catch 401 errors (refresh token expired)
|
||||||
return next.handle(authReq)
|
return next.handle(authReq)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err: HttpErrorResponse) => {
|
||||||
if (err.status === 401 && err.error && err.error.code === 'invalid_token') {
|
if (err.status === HttpStatusCode.UNAUTHORIZED_401 && err.error && err.error.code === 'invalid_token') {
|
||||||
return this.handleTokenExpired(req, next)
|
return this.handleTokenExpired(req, next)
|
||||||
|
} else if (err.status === HttpStatusCode.UNAUTHORIZED_401) {
|
||||||
|
return this.handleNotAuthenticated(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return observableThrowError(err)
|
return observableThrowError(err)
|
||||||
|
@ -51,6 +55,11 @@ export class AuthInterceptor implements HttpInterceptor {
|
||||||
// Clone the request to add the new header
|
// Clone the request to add the new header
|
||||||
return req.clone({ headers: req.headers.set('Authorization', authHeaderValue) })
|
return req.clone({ headers: req.headers.set('Authorization', authHeaderValue) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleNotAuthenticated (err: HttpErrorResponse, path = '/login'): Observable<any> {
|
||||||
|
this.router.navigateByUrl(path)
|
||||||
|
return of(err.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AUTH_INTERCEPTOR_PROVIDER = {
|
export const AUTH_INTERCEPTOR_PROVIDER = {
|
||||||
|
|
Loading…
Reference in New Issue