error logs
This commit is contained in:
@@ -1,11 +1,20 @@
|
||||
// /src/app/app.config.ts
|
||||
|
||||
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
|
||||
import {
|
||||
ApplicationConfig,
|
||||
importProvidersFrom,
|
||||
provideZoneChangeDetection,
|
||||
} from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
|
||||
import {
|
||||
provideHttpClient,
|
||||
withFetch,
|
||||
withInterceptors,
|
||||
} from '@angular/common/http';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { errorInterceptor } from './core/interceptors/error.interceptor';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
@@ -25,7 +34,7 @@ export const appConfig: ApplicationConfig = {
|
||||
// withFetch() entfernen, da es mit Interceptors noch Probleme geben kann
|
||||
// Stattdessen withInterceptors() verwenden
|
||||
provideHttpClient(
|
||||
withInterceptors([authInterceptor]) // Registriert unseren neuen Interceptor
|
||||
withInterceptors([authInterceptor, errorInterceptor]) // Registriert unseren neuen Interceptor
|
||||
),
|
||||
|
||||
// --- Forms Provider (sauberer Import) ---
|
||||
@@ -37,6 +46,6 @@ export const appConfig: ApplicationConfig = {
|
||||
|
||||
// --- API_URL Provider hinzufügen ---
|
||||
// Stellt den API_URL-Token mit dem Wert aus der passenden environment-Datei bereit
|
||||
{ provide: API_URL, useValue: environment.apiUrl }
|
||||
]
|
||||
};
|
||||
{ provide: API_URL, useValue: environment.apiUrl },
|
||||
],
|
||||
};
|
||||
|
||||
34
src/app/core/interceptors/error.interceptor.ts
Normal file
34
src/app/core/interceptors/error.interceptor.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
// /src/app/core/interceptors/error.interceptor.ts
|
||||
|
||||
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
|
||||
import { inject } from '@angular/core';
|
||||
import { catchError, throwError } from 'rxjs';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { LoggingService } from '../services/logging.service';
|
||||
|
||||
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const authService = inject(AuthService);
|
||||
const logger = inject(LoggingService);
|
||||
|
||||
return next(req).pipe(
|
||||
catchError((error: unknown) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
// Speziell den 401-Fehler (Unauthorized) abfangen
|
||||
if (error.status === 401) {
|
||||
logger.warn('Unauthorized request (401). Token might be expired or invalid. Logging out.');
|
||||
// Den Benutzer ausloggen und zur Login-Seite weiterleiten
|
||||
authService.logout();
|
||||
} else {
|
||||
// Andere Server-Fehler loggen
|
||||
logger.error(`HTTP Error: ${error.status} ${error.statusText}`, error);
|
||||
}
|
||||
} else {
|
||||
// Client-seitige Fehler loggen
|
||||
logger.error('An unknown client-side error occurred', error);
|
||||
}
|
||||
|
||||
// Den Fehler an den aufrufenden Service weitergeben
|
||||
return throwError(() => error);
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -5,11 +5,18 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable, BehaviorSubject, of } from 'rxjs';
|
||||
import { tap, catchError, map } from 'rxjs/operators';
|
||||
import { tap, catchError } from 'rxjs/operators';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
import { LoginRequest, AuthResponse, RegisterRequest } from '../models/auth.models'
|
||||
import { LoginRequest, AuthResponse, RegisterRequest } from '../models/auth.models';
|
||||
import { API_URL } from '../tokens/api-url.token';
|
||||
|
||||
// Ein Hilfs-Interface, um die Struktur des dekodierten Tokens typsicher zu machen.
|
||||
interface DecodedToken {
|
||||
exp: number; // Expiration time als UNIX-Timestamp in Sekunden
|
||||
// Hier könnten weitere Claims wie 'email', 'sub', 'role' etc. stehen
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@@ -18,30 +25,46 @@ export class AuthService {
|
||||
private http = inject(HttpClient);
|
||||
private router = inject(Router);
|
||||
private platformId = inject(PLATFORM_ID);
|
||||
private apiUrl = inject(API_URL); // <-- SAUBERE INJEKTION!
|
||||
private apiUrl = inject(API_URL);
|
||||
|
||||
private readonly endpoint = '/Auth';
|
||||
|
||||
// Keys für die Speicherung im localStorage
|
||||
private readonly TOKEN_KEY = 'auth-token';
|
||||
private readonly ROLES_KEY = 'auth-user-roles';
|
||||
|
||||
private loggedInStatus = new BehaviorSubject<boolean>(this.isBrowser() && !!this.getToken());
|
||||
public isLoggedIn$ = this.loggedInStatus.asObservable();
|
||||
|
||||
private tokenExpirationTimer: any;
|
||||
|
||||
constructor() {
|
||||
// Beim Initialisieren des Services prüfen, ob bereits ein Token vorhanden ist
|
||||
// und ggf. den Logout-Timer dafür starten (z.B. nach einem Neuladen der Seite).
|
||||
this.initTokenCheck();
|
||||
}
|
||||
|
||||
loginAdmin(credentials: LoginRequest): Observable<AuthResponse | null> {
|
||||
return this.http.post<AuthResponse>(`${this.apiUrl}${this.endpoint}/login/admin`, credentials).pipe(
|
||||
tap(response => this.setSession(response)),
|
||||
tap(response => {
|
||||
if (response?.isAuthSuccessful) {
|
||||
this.setSession(response);
|
||||
this.startTokenExpirationTimer(); // Timer nach erfolgreichem Login starten
|
||||
}
|
||||
}),
|
||||
catchError(() => {
|
||||
this.clearSession(); // Bei fehlgeschlagenem Login immer die Session aufräumen
|
||||
return of(null); // Gib ein "leeres" Observable zurück statt einen Fehler zu werfen
|
||||
this.clearSession();
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
loginCustomer(credentials: LoginRequest): Observable<AuthResponse | null> {
|
||||
return this.http.post<AuthResponse>(`${this.apiUrl}${this.endpoint}/login/customer`, credentials).pipe(
|
||||
tap(response => this.setSession(response)),
|
||||
tap(response => {
|
||||
if (response?.isAuthSuccessful) {
|
||||
this.setSession(response);
|
||||
this.startTokenExpirationTimer(); // Timer nach erfolgreichem Login starten
|
||||
}
|
||||
}),
|
||||
catchError(() => {
|
||||
this.clearSession();
|
||||
return of(null);
|
||||
@@ -57,7 +80,11 @@ export class AuthService {
|
||||
|
||||
logout(): void {
|
||||
this.clearSession();
|
||||
this.router.navigate(['/auth/login']); // Empfehlung: Routen gruppieren
|
||||
// Den proaktiven Timer stoppen, da der Logout manuell erfolgt.
|
||||
if (this.tokenExpirationTimer) {
|
||||
clearTimeout(this.tokenExpirationTimer);
|
||||
}
|
||||
this.router.navigate(['/auth/login']);
|
||||
}
|
||||
|
||||
getToken(): string | null {
|
||||
@@ -93,4 +120,41 @@ export class AuthService {
|
||||
private isBrowser(): boolean {
|
||||
return isPlatformBrowser(this.platformId);
|
||||
}
|
||||
|
||||
private startTokenExpirationTimer(): void {
|
||||
if (this.tokenExpirationTimer) {
|
||||
clearTimeout(this.tokenExpirationTimer);
|
||||
}
|
||||
|
||||
const token = this.getToken();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = jwtDecode<DecodedToken>(token);
|
||||
const expirationDate = new Date(decodedToken.exp * 1000); // JWT 'exp' ist in Sekunden, Date() braucht Millisekunden
|
||||
const timeoutDuration = expirationDate.getTime() - new Date().getTime();
|
||||
|
||||
if (timeoutDuration > 0) {
|
||||
this.tokenExpirationTimer = setTimeout(() => {
|
||||
console.warn('Sitzung proaktiv beendet, da das Token abgelaufen ist.');
|
||||
this.logout();
|
||||
// Hier könnte man eine Snackbar-Nachricht anzeigen
|
||||
}, timeoutDuration);
|
||||
} else {
|
||||
// Das gespeicherte Token ist bereits abgelaufen
|
||||
this.clearSession();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Dekodieren des Tokens. Session wird bereinigt.', error);
|
||||
this.clearSession();
|
||||
}
|
||||
}
|
||||
|
||||
private initTokenCheck(): void {
|
||||
if (this.isBrowser()) {
|
||||
this.startTokenExpirationTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user