import { Injectable, OnDestroy } from '@angular/core'; import { Observable, BehaviorSubject, ReplaySubject, Subscription } from 'rxjs'; import { map, distinctUntilChanged, take } from 'rxjs/operators'; import { User } from 'src/app/core/models'; import { ApiService, 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 apiService: ApiService, private 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 = this.apiService.get('/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?: any): Observable { let user$: Observable; if (type === 'authenticate') { user$ = this.apiService.get('/authenticate'); } else { const route = (type === 'login') ? '/login' : ''; user$ = this.apiService.post(`/users${route}`, { user: credentials }); } return user$.pipe(map( (data: any) => { this.setAuth(data.user); return data; } ));; } 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: any): Observable { return this.apiService .put('/user', { user }) .pipe(map(data => { // Update the currentUser observable this.currentUserSubject.next(data.user); return data.user; })); } }