Почтовый кабинет: регистрация, папки, черновики, вложения, грид @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:
2026-11-05 16:41:29 +01:00
parent f605fbfd85
commit 29b1beabe0
70 changed files with 3468 additions and 1737 deletions
+28
View File
@@ -0,0 +1,28 @@
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { catchError, throwError } from 'rxjs';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const router = inject(Router);
const token = auth.token();
const request = token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req;
return next(request).pipe(
catchError((error: unknown) => {
// not logged in or the token expired → to the login form
if (error instanceof HttpErrorResponse && error.status === 401 && !req.url.endsWith('/login')) {
auth.clear();
router.navigate(['/login']);
}
return throwError(() => error);
})
);
};
export const authGuard: CanActivateFn = () =>
inject(AuthService).loggedIn() ? true : inject(Router).createUrlTree(['/login']);
export const guestGuard: CanActivateFn = () =>
inject(AuthService).loggedIn() ? inject(Router).createUrlTree(['/']) : true;