added dashboard
This commit is contained in:
parent
ab1368553f
commit
9aec010316
|
@ -1,15 +1,33 @@
|
||||||
import { Component, inject } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet, Router } from '@angular/router';
|
||||||
|
|
||||||
import { AuthService } from './shared/service';
|
import { AuthService } from './shared/service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
providers: [AuthService],
|
providers: [],
|
||||||
imports: [RouterOutlet],
|
imports: [RouterOutlet],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss',
|
styleUrl: './app.component.scss',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit {
|
||||||
private readonly authService: AuthService = inject(AuthService);
|
public constructor(
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
private readonly router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.checkAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkAuthentication(): void {
|
||||||
|
this.authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
console.log('User is authenticated');
|
||||||
|
this.router.navigateByUrl('dashboard');
|
||||||
|
} else {
|
||||||
|
this.router.navigateByUrl('signup');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
export const routes: Routes = [
|
import { AuthGuard } from './shared/guard/auth.guard';
|
||||||
{ path: '', pathMatch: 'full', redirectTo: '' },
|
|
||||||
|
const publicRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadComponent: () => import('./app.component').then((m) => m.AppComponent),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'signup',
|
path: 'signup',
|
||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
|
@ -17,3 +22,25 @@ export const routes: Routes = [
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const protectedRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'dashboard',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./pages/dashboard-root/dashboard-root.component').then(
|
||||||
|
(m) => m.DashboardRootComponent
|
||||||
|
),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
children: [
|
||||||
|
...publicRoutes,
|
||||||
|
...protectedRoutes,
|
||||||
|
{ path: '', redirectTo: '', pathMatch: 'full' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Hello World</h1>
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dashboard-root',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
providers: [],
|
||||||
|
templateUrl: './dashboard-root.component.html',
|
||||||
|
styleUrl: './dashboard-root.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class DashboardRootComponent {
|
||||||
|
public constructor() {}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ type AuthAction = 'register' | 'signup';
|
||||||
PasswordModule,
|
PasswordModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
],
|
],
|
||||||
providers: [AuthService],
|
providers: [],
|
||||||
templateUrl: './register-root.component.html',
|
templateUrl: './register-root.component.html',
|
||||||
styleUrl: './register-root.component.scss',
|
styleUrl: './register-root.component.scss',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivateFn,
|
||||||
|
Router,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
UrlTree,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { AuthService } from '../service';
|
||||||
|
|
||||||
|
export const AuthGuard: CanActivateFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
):
|
||||||
|
| Observable<boolean | UrlTree>
|
||||||
|
| Promise<boolean | UrlTree>
|
||||||
|
| boolean
|
||||||
|
| UrlTree => {
|
||||||
|
const authService: AuthService = inject(AuthService);
|
||||||
|
const router: Router = inject(Router);
|
||||||
|
|
||||||
|
authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
router.navigateByUrl('signup');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
|
@ -1,51 +1,79 @@
|
||||||
import {
|
import {
|
||||||
HttpErrorResponse,
|
|
||||||
HttpEvent,
|
|
||||||
HttpHandlerFn,
|
|
||||||
HttpInterceptorFn,
|
HttpInterceptorFn,
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
|
HttpHandlerFn,
|
||||||
|
HttpEvent,
|
||||||
|
HttpErrorResponse,
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { Observable, catchError, switchMap, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AuthService } from '../service';
|
import { AuthService } from '../service';
|
||||||
import { Tokens } from '../types';
|
|
||||||
|
|
||||||
export const AuthInterceptor: HttpInterceptorFn = (
|
export const AuthInterceptor: HttpInterceptorFn = (
|
||||||
request: HttpRequest<unknown>,
|
request: HttpRequest<unknown>,
|
||||||
next: HttpHandlerFn
|
next: HttpHandlerFn
|
||||||
): Observable<HttpEvent<unknown>> => {
|
): Observable<HttpEvent<unknown>> => {
|
||||||
const authService: AuthService = inject(AuthService);
|
const router = inject(Router);
|
||||||
const accessToken: string | null = authService.access_token;
|
const authService = inject(AuthService);
|
||||||
|
|
||||||
|
const handleRequest = (
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
const accessToken = authService.access_token;
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
request = request.clone({
|
req = addAuthHeader(req, accessToken);
|
||||||
setHeaders: {
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return next(request).pipe(
|
return next(req);
|
||||||
catchError((error: HttpErrorResponse) => {
|
};
|
||||||
if (error.status === 401) {
|
|
||||||
return authService.refreshToken().pipe(
|
const addAuthHeader = (
|
||||||
switchMap((tokens: Tokens) => {
|
req: HttpRequest<unknown>,
|
||||||
request = request.clone({
|
token: string
|
||||||
|
): HttpRequest<unknown> => {
|
||||||
|
return req.clone({
|
||||||
setHeaders: {
|
setHeaders: {
|
||||||
Authorization: `Bearer ${tokens.access_token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return next(request);
|
};
|
||||||
|
|
||||||
|
const handle401Error = (
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
console.log(authService.refresh_token);
|
||||||
|
if (!authService.refresh_token) {
|
||||||
|
router.navigateByUrl('signup');
|
||||||
|
return throwError(() => new Error('Authentication required'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return authService.refreshToken().pipe(
|
||||||
|
switchMap((tokens) => {
|
||||||
|
req = addAuthHeader(req, tokens.access_token);
|
||||||
|
return next(req);
|
||||||
}),
|
}),
|
||||||
catchError((refreshError) => {
|
catchError((refreshError) => {
|
||||||
authService.signout();
|
router.navigateByUrl('signup');
|
||||||
return throwError(() => new Error(refreshError));
|
return throwError(() => new Error(refreshError));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
return throwError(() => new Error());
|
const handleError = (
|
||||||
})
|
error: HttpErrorResponse,
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
if (error.status === 401) {
|
||||||
|
return handle401Error(req);
|
||||||
|
}
|
||||||
|
return throwError(() => new Error('Unhandled error'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return handleRequest(request).pipe(
|
||||||
|
catchError((error) => handleError(error, request))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
||||||
|
@ -13,22 +12,28 @@ import { SessionStorageService } from './session-storage.service';
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
public isAuthenticated$: BehaviorSubject<boolean> =
|
||||||
|
new BehaviorSubject<boolean>(false);
|
||||||
private _access_token: string | null = null;
|
private _access_token: string | null = null;
|
||||||
private _refresh_token: string | null = null;
|
private _refresh_token: string | null = null;
|
||||||
private _isAuthenticated$: BehaviorSubject<boolean> =
|
|
||||||
new BehaviorSubject<boolean>(false);
|
|
||||||
|
|
||||||
public get access_token(): string | null {
|
public get access_token(): string | null {
|
||||||
return this._access_token;
|
return this._access_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get refresh_token(): string | null {
|
||||||
|
return this._refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly httpClient: HttpClient,
|
|
||||||
private readonly localStorageService: LocalStorageService,
|
private readonly localStorageService: LocalStorageService,
|
||||||
private readonly sessionStorageService: SessionStorageService,
|
private readonly sessionStorageService: SessionStorageService,
|
||||||
private readonly authenticationApiService: AuthenticationApiService
|
private readonly authenticationApiService: AuthenticationApiService
|
||||||
) {
|
) {
|
||||||
//this.autoLogin();
|
this._access_token =
|
||||||
|
this.localStorageService.getItem<string>('access_token');
|
||||||
|
this._refresh_token =
|
||||||
|
this.sessionStorageService.getItem<string>('refresh_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
public signin(credentials: LoginCredentials): void {
|
public signin(credentials: LoginCredentials): void {
|
||||||
|
@ -66,31 +71,16 @@ export class AuthService {
|
||||||
this._refresh_token = null;
|
this._refresh_token = null;
|
||||||
this.localStorageService.removeItem('access_token');
|
this.localStorageService.removeItem('access_token');
|
||||||
this.sessionStorageService.removeItem('refresh_token');
|
this.sessionStorageService.removeItem('refresh_token');
|
||||||
this._isAuthenticated$.next(false);
|
this.isAuthenticated$.next(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public autoLogin(): void {
|
|
||||||
const storedAccessToken: string | null =
|
|
||||||
this.localStorageService.getItem('access_token');
|
|
||||||
const storedRefreshToken: string | null =
|
|
||||||
this.sessionStorageService.getItem('refresh_token');
|
|
||||||
|
|
||||||
if (storedAccessToken && storedRefreshToken) {
|
|
||||||
this._refresh_token = storedRefreshToken;
|
|
||||||
this._isAuthenticated$.next(true);
|
|
||||||
//TODO Validate tokens with backend or decode JWT to check expiration
|
|
||||||
} else {
|
|
||||||
this.signout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleSuccess(tokens: Tokens): void {
|
private handleSuccess(tokens: Tokens): void {
|
||||||
this._access_token = tokens.access_token;
|
this._access_token = tokens.access_token;
|
||||||
this._refresh_token = tokens.refresh_token;
|
this._refresh_token = tokens.refresh_token;
|
||||||
this.localStorageService.setItem('access_token', tokens.access_token);
|
this.localStorageService.setItem('access_token', tokens.access_token);
|
||||||
this.sessionStorageService.setItem('refresh_token', tokens.refresh_token);
|
this.sessionStorageService.setItem('refresh_token', tokens.refresh_token);
|
||||||
this._isAuthenticated$.next(true);
|
this.isAuthenticated$.next(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue