|
1 | | -import { HttpInterceptorFn } from '@angular/common/http'; |
| 1 | +import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http'; |
| 2 | +import { inject } from '@angular/core'; |
2 | 3 | import { throwError } from 'rxjs'; |
3 | 4 | import { catchError } from 'rxjs/operators'; |
| 5 | +import { UserService } from '../auth/services/user.service'; |
4 | 6 |
|
| 7 | +/** |
| 8 | + * Global HTTP error interceptor. |
| 9 | + * |
| 10 | + * ## 401 Handling Strategy |
| 11 | + * |
| 12 | + * There are two layers of 401 handling: |
| 13 | + * |
| 14 | + * 1. GET /user endpoint (auth initialization): |
| 15 | + * - Handled by UserService.getCurrentUser() with 4XX vs 5XX logic |
| 16 | + * - This interceptor SKIPS /user to avoid double-handling |
| 17 | + * |
| 18 | + * 2. All OTHER endpoints (articles, comments, profiles, etc.): |
| 19 | + * - Handled HERE - any 401 means "token expired mid-session" |
| 20 | + * - We call purgeAuth() to logout immediately |
| 21 | + * |
| 22 | + * Both paths result in logout on 401, but /user needs special handling |
| 23 | + * because it also distinguishes 5XX errors (server down → retry). |
| 24 | + * |
| 25 | + * ## Error Format |
| 26 | + * |
| 27 | + * Re-throws errors with { ...body, status } format so that: |
| 28 | + * - Components can access err.errors for display (e.g., validation errors) |
| 29 | + * - Auth logic can check err.status for error type decisions |
| 30 | + * - Network errors get a user-friendly fallback message |
| 31 | + */ |
5 | 32 | export const errorInterceptor: HttpInterceptorFn = (req, next) => { |
6 | | - return next(req).pipe(catchError(err => throwError(() => err.error))); |
| 33 | + const userService = inject(UserService); |
| 34 | + |
| 35 | + return next(req).pipe( |
| 36 | + catchError((err: HttpErrorResponse) => { |
| 37 | + // Global 401 handling for all endpoints EXCEPT /user |
| 38 | + // (token expired mid-session → logout) |
| 39 | + // /user is handled by UserService.getCurrentUser() with 4XX vs 5XX logic |
| 40 | + if (err.status === 401 && !req.url.endsWith('/user')) { |
| 41 | + userService.purgeAuth(); |
| 42 | + } |
| 43 | + |
| 44 | + // Normalize error format: { errors: {...}, status: number } |
| 45 | + // Provides fallback message for network errors (status 0) or missing body |
| 46 | + const body = err.error && typeof err.error === 'object' && 'errors' in err.error |
| 47 | + ? err.error |
| 48 | + : { errors: { network: ['Unable to connect. Please check your internet connection.'] } }; |
| 49 | + |
| 50 | + return throwError(() => ({ ...body, status: err.status })); |
| 51 | + }), |
| 52 | + ); |
7 | 53 | }; |
0 commit comments