import { Injectable, computed, inject, signal } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { Observable, tap } from 'rxjs'; interface TokenResponse { token: string; email: string; } /** JWT in localStorage; who is logged in. */ @Injectable({ providedIn: 'root' }) export class AuthService { private http = inject(HttpClient); private router = inject(Router); readonly email = signal(read('email')); /** Administrator who opened this mailbox (Settings → MAILBOXES → Open), or null */ readonly openedBy = signal(read('adminEmail')); readonly loggedIn = computed(() => this.email() !== null && read('token') !== null); login(email: string, password: string): Observable { return this.http.post('/api/login', { email, password }).pipe(tap(r => this.store(r))); } register(email: string, password: string, displayName: string): Observable { return this.http.post('/api/register', { email, password, displayName }).pipe(tap(r => this.store(r))); } token(): string | null { return read('token'); } /** Administrator opens another mailbox: its session, the admin session kept to come back. */ openAs(response: TokenResponse): void { const adminToken = read('token'); const adminEmail = read('email'); try { if (adminToken && adminEmail && !read('adminToken')) { localStorage.setItem('adminToken', adminToken); localStorage.setItem('adminEmail', adminEmail); } } catch { /* storage blocked */ } this.store(response); this.openedBy.set(read('adminEmail')); } /** * Switch to another mailbox of the same login (left menu). Going back to the mailbox an administrator came * from ends the administrator's visit. */ switchTo(response: TokenResponse): void { if (this.openedBy() && response.email === this.openedBy()) { this.backToAdmin(); return; } this.store(response); } /** Back to the administrator's own mailbox. */ backToAdmin(): void { const token = read('adminToken'); const email = read('adminEmail'); try { localStorage.removeItem('adminToken'); localStorage.removeItem('adminEmail'); } catch { /* storage blocked */ } this.openedBy.set(null); if (token && email) { this.store({ token, email }); } else { this.clear(); } } logout(): void { this.clear(); this.router.navigate(['/login']); } clear(): void { try { localStorage.removeItem('token'); localStorage.removeItem('email'); localStorage.removeItem('adminToken'); localStorage.removeItem('adminEmail'); } catch { /* storage blocked */ } this.email.set(null); this.openedBy.set(null); } private store(response: TokenResponse): void { try { localStorage.setItem('token', response.token); localStorage.setItem('email', response.email); } catch { /* storage blocked: logged in for this tab only */ } this.email.set(response.email); } } function read(key: string): string | null { try { return localStorage.getItem(key); } catch { return null; } }