feat: add Master Admin application functionality
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { computed, Injectable, inject, signal } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, catchError, finalize, map, shareReplay, throwError } from 'rxjs';
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { LoginRequest, LoginResponse, UserProfile } from '../../models/auth/auth.model';
|
||||
import { TokenStorageService } from './token-storage.service';
|
||||
import { AppContextService } from '../../services/context/app-context.service';
|
||||
import { AUTH_ENDPOINTS } from '../../../core/end-points/auth/auth.endpoints';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthService {
|
||||
private readonly http = inject(HttpClient);
|
||||
private readonly tokenStorage = inject(TokenStorageService);
|
||||
private readonly appContextService = inject(AppContextService);
|
||||
|
||||
private readonly userSubject = new BehaviorSubject<UserProfile | null>(null);
|
||||
readonly user$ = this.userSubject.asObservable();
|
||||
readonly currentUserSignal = signal<UserProfile | null>(null);
|
||||
private readonly accessTokenSignal = signal<string | null>(null);
|
||||
readonly isAuthenticatedSignal = computed(() => !!this.accessTokenSignal() && !!this.currentUserSignal());
|
||||
|
||||
private refreshRequest$: Observable<LoginResponse> | null = null;
|
||||
|
||||
constructor() {
|
||||
this.restoreAuthState();
|
||||
}
|
||||
|
||||
login(payload: LoginRequest, rememberMe: boolean): Observable<LoginResponse> {
|
||||
return this.http.post<LoginResponse>(`${AUTH_ENDPOINTS.login}`, payload).pipe(
|
||||
map((response) => {
|
||||
const user = this.normalizeUserProfile(response);
|
||||
this.tokenStorage.saveAuth(response, rememberMe);
|
||||
this.setAuthState(user, this.tokenStorage.getAccessToken());
|
||||
return response;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
refreshAccessToken(): Observable<LoginResponse> {
|
||||
if (this.refreshRequest$) {
|
||||
return this.refreshRequest$;
|
||||
}
|
||||
|
||||
const refreshToken = this.tokenStorage.getRefreshToken();
|
||||
if (!refreshToken) {
|
||||
this.logout();
|
||||
return throwError(() => new HttpErrorResponse({ status: 401, statusText: 'Refresh token missing' }));
|
||||
}
|
||||
|
||||
this.refreshRequest$ = this.http.post<LoginResponse>(`${AUTH_ENDPOINTS.refresh}`, { refreshToken }).pipe(
|
||||
map((response) => {
|
||||
const user = this.normalizeUserProfile(response, this.currentUserSignal());
|
||||
const storageType = this.tokenStorage.getStorageType();
|
||||
const rememberMe = storageType === 'local';
|
||||
this.tokenStorage.saveAuth(response, rememberMe);
|
||||
this.setAuthState(user, this.tokenStorage.getAccessToken());
|
||||
return response;
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.logout();
|
||||
return throwError(() => error);
|
||||
}),
|
||||
finalize(() => {
|
||||
this.refreshRequest$ = null;
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
return this.refreshRequest$;
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.tokenStorage.clearAuth();
|
||||
this.appContextService.clearContext();
|
||||
this.setAuthState(null, null);
|
||||
this.refreshRequest$ = null;
|
||||
}
|
||||
|
||||
get accessToken(): string | null {
|
||||
return this.tokenStorage.getAccessToken();
|
||||
}
|
||||
|
||||
get refreshToken(): string | null {
|
||||
return this.tokenStorage.getRefreshToken();
|
||||
}
|
||||
|
||||
get isAuthenticated(): boolean {
|
||||
return this.isAuthenticatedSignal();
|
||||
}
|
||||
|
||||
get isLoggedIn(): boolean {
|
||||
return this.isAuthenticatedSignal();
|
||||
}
|
||||
|
||||
get currentUser(): UserProfile | null {
|
||||
return this.currentUserSignal();
|
||||
}
|
||||
|
||||
private restoreAuthState(): void {
|
||||
const storedUser = this.tokenStorage.getUser();
|
||||
const storedAccessToken = this.tokenStorage.getAccessToken();
|
||||
|
||||
if (storedUser && storedAccessToken) {
|
||||
this.setAuthState(storedUser, storedAccessToken);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAuthState(null, storedAccessToken ?? null);
|
||||
}
|
||||
|
||||
private setAuthState(user: UserProfile | null, token: string | null): void {
|
||||
this.userSubject.next(user);
|
||||
this.currentUserSignal.set(user);
|
||||
this.accessTokenSignal.set(token);
|
||||
}
|
||||
|
||||
private normalizeUserProfile(response: LoginResponse, fallbackUser: UserProfile | null = null): UserProfile | null {
|
||||
if (response.user) {
|
||||
return response.user;
|
||||
}
|
||||
|
||||
const userId = response.userId?.trim();
|
||||
const email = response.email?.trim();
|
||||
if (userId || email) {
|
||||
return {
|
||||
id: userId ?? fallbackUser?.id ?? '',
|
||||
email: email ?? fallbackUser?.email ?? '',
|
||||
roles: response.roles,
|
||||
};
|
||||
}
|
||||
|
||||
return fallbackUser;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user