Почтовый кабинет: регистрация, папки, черновики, вложения, грид @jambotron/aria-grid, интерфейс по synthwave-шаблону
Бэкенд: регистрация со входом сразу и /api/config, профиль и смена пароля, свои папки, PATCH письма (прочитано, флажок, перенос; Спам ставит отметку), корзина и очистка, черновики, несколько адресатов и копия, вложения черновика уходят с письмом, вложения и заголовки To/Cc входящих сохраняются, письма заблокированных — в Спам. Исправлено: загрузка файла (415), колонка content_id (500 на любых вложениях), SPA-маршрут с точкой. Фронтенд переписан: все таблицы — грид, оформление — шаблон synthwave-design-showcase.html. α Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
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<string | null>(read('email'));
|
||||
readonly loggedIn = computed(() => this.email() !== null && read('token') !== null);
|
||||
|
||||
login(email: string, password: string): Observable<TokenResponse> {
|
||||
return this.http.post<TokenResponse>('/api/login', { email, password }).pipe(tap(r => this.store(r)));
|
||||
}
|
||||
|
||||
register(email: string, password: string, displayName: string): Observable<TokenResponse> {
|
||||
return this.http.post<TokenResponse>('/api/register', { email, password, displayName }).pipe(tap(r => this.store(r)));
|
||||
}
|
||||
|
||||
token(): string | null { return read('token'); }
|
||||
|
||||
logout(): void {
|
||||
this.clear();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
try {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('email');
|
||||
} catch { /* storage blocked */ }
|
||||
this.email.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; }
|
||||
}
|
||||
Reference in New Issue
Block a user