diff --git a/client/src/app/+error-page/error-page.component.html b/client/src/app/+error-page/error-page.component.html index 6a1675f90..83de1f84a 100644 --- a/client/src/app/+error-page/error-page.component.html +++ b/client/src/app/+error-page/error-page.component.html @@ -1,58 +1,74 @@
-
- {{ status }}. - That's an error. + @if (status === 401) { +
+ {{ status }}. + You are not authorized here. -
- We couldn't find any video tied to the URL {{ pathname }} you were looking for. - We couldn't find any resource tied to the URL {{ pathname }} you were looking for. +
+ @if (type === 'video') { + You might need to login to see the video. + } @else { + You might need to login to see the resource. + } +
+ +
+ } @else if (status === 403) { +
+ {{ status }}. + You are not authorized here. -
- Possible reasons: - -
    -
  • You may have used an outdated or broken link
  • -
  • - The video may have been moved or deleted - The resource may have been moved or deleted -
  • -
  • You may have typed the address or URL incorrectly
  • -
+
+ @if (type === 'video') { + You might need to check your account is allowed by the video or instance owner. + } @else { + You might need to check your account is allowed by the resource or instance owner. + } +
-
+ } @else if (status === 418) { +
+ {{ status }}. + I'm a teapot. -
- {{ status }}. - You are not authorized here. - -
- You might need to login to see the video. - You might need to login to see the resource. +
+ The requested entity body blends sweet bits with a mellow earthiness. +
+
Sepia seems to like it.
+ } @else { +
+ {{ status }}. + That's an error. - -
+
+ @if (type === 'video') { + We couldn't find any video tied to the URL {{ pathname }} you were looking for. + } @else { + We couldn't find any resource tied to the URL {{ pathname }} you were looking for. + } +
-
- {{ status }}. - You are not authorized here. +
+ Possible reasons: -
- You might need to check your account is allowed by the video or instance owner. - You might need to check your account is allowed by the resource or instance owner. +
    +
  • You may have used an outdated or broken link
  • + +
  • + @if (type === 'video') { + The video may have been moved or deleted + } @else { + The resource may have been moved or deleted + } +
  • + +
  • You may have typed the address or URL incorrectly
  • +
+
-
- -
- {{ status }}. - I'm a teapot. - -
- The requested entity body blends sweet bits with a mellow earthiness. -
-
Sepia seems to like it.
-
+ } {{ status }} mascot
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index c81d2ac65..1279f19b0 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -60,12 +60,12 @@
+ +

{{ message.summary }}

{{ message.detail }}

- -
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 736ee7359..52f9d420c 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -294,12 +294,12 @@ export class AppComponent implements OnInit, AfterViewInit { // Admin modal userSub.pipe( filter(user => user.role.id === UserRole.ADMINISTRATOR) - ).subscribe(user => this.openAdminModalsIfNeeded(user)) + ).subscribe(user => setTimeout(() => this.openAdminModalsIfNeeded(user))) // Wait deferred modal creation in the view // Account modal userSub.pipe( filter(user => user.role.id !== UserRole.ADMINISTRATOR) - ).subscribe(user => this.openAccountModalsIfNeeded(user)) + ).subscribe(user => setTimeout(() => this.openAccountModalsIfNeeded(user))) // Wait deferred modal creation in the view } private openAdminModalsIfNeeded (user: User) { diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 341ff8637..1685e298e 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -1,15 +1,14 @@ -import { Hotkey, HotkeysService } from '@app/core' -import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' -import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { Router } from '@angular/router' +import { Hotkey, HotkeysService } from '@app/core' import { Notifier } from '@app/core/notification/notifier.service' +import { HttpStatusCode, OAuthClientLocal, User, UserLogin, UserRefreshToken, MyUser as UserServerModel } from '@peertube/peertube-models' import { logger, OAuthUserTokens, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' -import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@peertube/peertube-models' +import { Observable, of, ReplaySubject, Subject, throwError } from 'rxjs' +import { catchError, map, mergeMap, share, tap } from 'rxjs/operators' import { environment } from '../../../environments/environment' import { RestExtractor } from '../rest/rest-extractor.service' -import { RedirectService } from '../routing' import { AuthStatus } from './auth-status.model' import { AuthUser } from './auth-user.model' @@ -42,10 +41,9 @@ export class AuthService { private clientSecret: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET) private loginChanged: Subject private user: AuthUser = null - private refreshingTokenObservable: Observable + private refreshingTokenObservable: Observable constructor ( - private redirectService: RedirectService, private http: HttpClient, private notifier: Notifier, private hotkeysService: HotkeysService, @@ -180,18 +178,20 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular logout () { const authHeaderValue = this.getRequestHeaderValue() - const headers = new HttpHeaders().set('Authorization', authHeaderValue) - this.http.post<{ redirectUrl?: string }>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers }) - .subscribe({ - next: res => { - if (res.redirectUrl) { - window.location.href = res.redirectUrl - } - }, + const obs: Observable<{ redirectUrl?: string }> = authHeaderValue + ? this.http.post(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers: new HttpHeaders().set('Authorization', authHeaderValue) }) + : of({}) - error: err => logger.error(err) - }) + obs.subscribe({ + next: res => { + if (res.redirectUrl) { + window.location.href = res.redirectUrl + } + }, + + error: err => logger.error(err) + }) this.user = null @@ -200,6 +200,7 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular refreshAccessToken () { if (this.refreshingTokenObservable) return this.refreshingTokenObservable + if (!this.getAccessToken()) return throwError(() => new Error($localize`You need to reconnect`)) logger.info('Refreshing token...') @@ -221,17 +222,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular this.refreshingTokenObservable = null }), catchError(err => { - this.refreshingTokenObservable = null - - logger.error(err) - logger.info('Cannot refresh token -> logout...') + logger.clientError(err) this.logout() - this.redirectService.redirectToLogin() + this.notifier.info($localize`Your authentication has expired, you need to reconnect.`, undefined, undefined, true) + this.refreshingTokenObservable = null - return observableThrowError(() => ({ - error: $localize`You need to reconnect.` - })) + return throwError(() => new Error($localize`You need to reconnect`)) }), share() ) diff --git a/client/src/app/core/routing/login-guard.service.ts b/client/src/app/core/routing/login-guard.service.ts index ecb902625..be0ced881 100644 --- a/client/src/app/core/routing/login-guard.service.ts +++ b/client/src/app/core/routing/login-guard.service.ts @@ -14,7 +14,10 @@ export class LoginGuard { canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (this.auth.isLoggedIn() === true) return true - this.redirectService.redirectToLogin() + const err = new Error('') as any + err.status = 401 + + this.redirectService.replaceBy401(err) return false } diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index ca9c0cbf2..a278d147f 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts @@ -112,6 +112,10 @@ export class RedirectService { else this.router.navigate([ '/login' ]) } + replaceBy401 (err: Error) { + this.router.navigate([ '/401' ], { state: { obj: err }, skipLocationChange: true }) + } + private doRedirect (redirectUrl: string, fallbackRoute?: string) { debugLogger('Redirecting on %s', redirectUrl) diff --git a/client/src/app/core/routing/user-right-guard.service.ts b/client/src/app/core/routing/user-right-guard.service.ts index 9f293f628..33e767467 100644 --- a/client/src/app/core/routing/user-right-guard.service.ts +++ b/client/src/app/core/routing/user-right-guard.service.ts @@ -19,7 +19,11 @@ export class UserRightGuard { if (user.hasRight(neededUserRight)) return true } - this.redirectService.redirectToLogin() + const err = new Error('') as any + err.status = 403 + + this.redirectService.replaceBy401(err) + return false } diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss index 55a802481..1f3cb7890 100644 --- a/client/src/sass/primeng-custom.scss +++ b/client/src/sass/primeng-custom.scss @@ -1088,7 +1088,7 @@ p-toast { min-width: 200px; .p-toast-icon-close { - opacity: 0; + color: pvar(--greyForegroundColor); position: absolute; right: 5px; top: 5px; @@ -1097,10 +1097,6 @@ p-toast { background: url('../assets/images/feather/x.svg') no-repeat; background-size: contain; } - - &:hover .p-toast-icon-close { - opacity: .3; - } } .p-toast-message { @@ -1117,10 +1113,10 @@ p-toast { display: flex; align-items: center; width: 100%; - padding: 10px 20px; + padding: 10px; .message { - @include margin-right(20px); + @include margin-left(10px); flex-grow: 1;