import { Injectable, OnDestroy } from '@angular/core'; import { HttpClient } from "@angular/common/http"; import { Observable, BehaviorSubject, ReplaySubject, Subscription } from 'rxjs'; import { distinctUntilChanged, take, tap } from 'rxjs/operators'; import { User } from 'src/app/core/models'; import { JwtService } from 'src/app/core/services'; @Injectable({ providedIn: 'root' }) export class UserService implements OnDestroy { private _subcriptions: Array = new Array(); private _user: Subscription = new Subscription(); private currentUserSubject = new BehaviorSubject({} as User); public currentUser = this.currentUserSubject.asObservable().pipe(distinctUntilChanged()); private isAuthenticatedSubject = new ReplaySubject(1); public isAuthenticated = this.isAuthenticatedSubject.asObservable(); constructor( private readonly http: HttpClient, private readonly jwtService: JwtService ) { } ngOnDestroy() { this._subcriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); } // Verify JWT in localstorage with server & load user's info. // This runs once on application startup. populate() { // If JWT detected, attempt to get & store user's info if (this.jwtService.getToken()) { const user$: Observable<{ user: User }> = this.http.get<{ user: User }>('/user').pipe(take(1)); this._user = user$.subscribe({ next: (data) => this.setAuth(data.user), error: () => this.purgeAuth() }); this._subcriptions.push(this._user); } else { // Remove any potential remnants of previous auth states this.purgeAuth(); } } setAuth(user: User) { // Save JWT sent from server in localstorage this.jwtService.saveToken(user.token); // Set current user data into observable this.currentUserSubject.next(user); // Set isAuthenticated to true this.isAuthenticatedSubject.next(true); } purgeAuth() { // Remove JWT from localstorage this.jwtService.destroyToken(); // Set current user to an empty object this.currentUserSubject.next({} as User); // Set auth status to false this.isAuthenticatedSubject.next(false); } attemptAuth(type: string, credentials?: { email: string; password: string; }): Observable<{ user: User }> { const route = (type === 'login') ? '/login' : ''; const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`/users${route}`, { user: credentials }); /*return user$.pipe(map( (data: any) => { this.setAuth(data.user); return data; } ));*/ return user$.pipe(tap(({ user }) => this.setAuth(user))); } getCurrentUser(): User { return this.currentUserSubject.value; } canAdministrate(): boolean { return (this.currentUserSubject.value.role == 'Admin') ? true : false; } // Update the user on the server (email, pass, etc) update(user: Partial): Observable<{ user: User }> { return this.http.put<{ user: User }>("/user", { user }).pipe( tap(({ user }) => { this.currentUserSubject.next(user); }), ); } }