/** * @copyright 2026 NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { Injectable, signal } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, tap, catchError, switchMap, throwError, of } from 'rxjs'; import { ApiService } from './api.service'; import { AuthResponse, User } from '@library/shared-types'; import { environment } from '../../environments/environment'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class AuthService { private currentUser = signal(null); public readonly user = this.currentUser.asReadonly(); private refreshing = false; constructor( private api: ApiService, private router: Router, private http: HttpClient ) {} login(): void { // Redirect to API login endpoint window.location.href = `${environment.apiUrl}/auth/login`; } getCurrentUser(): Observable { return this.api.get('/auth/me').pipe( tap(response => { this.currentUser.set(response.user); }), catchError(error => { if (error.status === 401) { return this.refreshToken().pipe( switchMap(() => this.api.get('/auth/me')), tap(response => { this.currentUser.set(response.user); }), catchError(() => { this.currentUser.set(null); return throwError(() => error); }) ); } return throwError(() => error); }) ); } refreshToken(): Observable { if (this.refreshing) { return of({ user: this.currentUser()!, accessToken: '' }); } this.refreshing = true; return this.http.post( `${environment.apiUrl}/auth/refresh`, {}, { withCredentials: true } ).pipe( tap(response => { this.currentUser.set(response.user); this.refreshing = false; }), catchError(error => { this.refreshing = false; this.currentUser.set(null); return throwError(() => error); }) ); } logout(): Observable<{ message: string }> { return this.api.post<{ message: string }>('/auth/logout', {}).pipe( tap(() => { this.currentUser.set(null); this.api.clearCsrfToken(); this.router.navigate(['/']); }) ); } clearUser(): void { this.currentUser.set(null); } isAuthenticated(): boolean { return this.user() !== null; } isAdmin(): boolean { return this.user()?.isAdmin === true; } }