mirror of
https://github.com/a-mayb3/KanbanCloneAngular.git
synced 2026-03-21 18:05:38 +01:00
Adapted some config for the api
This commit is contained in:
parent
773fa7ad6d
commit
d1e016b7df
9 changed files with 74 additions and 54 deletions
|
|
@ -1,7 +1,11 @@
|
||||||
import { ApplicationConfig, provideBrowserGlobalErrorListeners, APP_INITIALIZER } from '@angular/core';
|
import {
|
||||||
|
ApplicationConfig,
|
||||||
|
provideBrowserGlobalErrorListeners,
|
||||||
|
APP_INITIALIZER,
|
||||||
|
} from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||||
import { catchError, of } from 'rxjs';
|
import { catchError, of, throwError } from 'rxjs';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { httpInterceptor } from './interceptors/http.interceptor';
|
import { httpInterceptor } from './interceptors/http.interceptor';
|
||||||
|
|
@ -11,28 +15,30 @@ import { AuthService } from './services/auth.service';
|
||||||
* Initialize auth state on app startup by checking for existing session
|
* Initialize auth state on app startup by checking for existing session
|
||||||
*/
|
*/
|
||||||
function initializeAuth(authService: AuthService) {
|
function initializeAuth(authService: AuthService) {
|
||||||
return () => authService.checkSession().pipe(
|
return () =>
|
||||||
catchError((error) => {
|
authService.checkSession().pipe(
|
||||||
// Session check failed - user is not logged in or session expired
|
catchError((error) => {
|
||||||
console.log('No active session or session expired');
|
console.error('Session check failed:', error);
|
||||||
authService.clearAuthState();
|
if (error?.status === 401 || error?.status === 422) {
|
||||||
return of(null);
|
authService.clearAuthState();
|
||||||
})
|
return of(null);
|
||||||
);
|
}
|
||||||
|
authService.clearAuthState();
|
||||||
|
return throwError(() => error);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
provideHttpClient(
|
provideHttpClient(withInterceptors([httpInterceptor])),
|
||||||
withInterceptors([httpInterceptor])
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initializeAuth,
|
useFactory: initializeAuth,
|
||||||
deps: [AuthService],
|
deps: [AuthService],
|
||||||
multi: true
|
multi: true,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
<app-navbar />
|
<app-navbar />
|
||||||
<main class="main"></main>
|
<main class="main">
|
||||||
|
<router-outlet />
|
||||||
|
</main>
|
||||||
<app-footer />
|
<app-footer />
|
||||||
|
|
||||||
<router-outlet />
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,19 @@ export const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent)
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/new',
|
||||||
|
loadComponent: () => import('./pages/project-create/project-create.component').then(m => m.ProjectCreateComponent)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects/:id',
|
||||||
|
loadComponent: () => import('./pages/project-details/project-details.component').then(m => m.ProjectDetailsComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@ import { Component, signal, inject } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { AuthService } from './services/auth.service';
|
import { AuthService } from './services/auth.service';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Navbar } from './navbar/navbar';
|
import { NavbarComponent } from './components/navbar/navbar.component';
|
||||||
|
import { FooterComponent } from './components/footer/footer.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet, CommonModule, Navbar],
|
imports: [RouterOutlet, CommonModule, NavbarComponent, FooterComponent],
|
||||||
templateUrl: './app.html',
|
templateUrl: './app.html',
|
||||||
styleUrl: './app.css'
|
styleUrl: './app.css'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -15,33 +15,24 @@ export const httpInterceptor: HttpInterceptorFn = (req, next) => {
|
||||||
// Clone the request to add withCredentials flag
|
// Clone the request to add withCredentials flag
|
||||||
// This ensures cookies are sent with every request
|
// This ensures cookies are sent with every request
|
||||||
const reqWithCredentials = req.clone({
|
const reqWithCredentials = req.clone({
|
||||||
withCredentials: true
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pass the cloned request to the next handler
|
// Pass the cloned request to the next handler
|
||||||
return next(reqWithCredentials).pipe(
|
return next(reqWithCredentials).pipe(
|
||||||
catchError((error: HttpErrorResponse) => {
|
catchError((error: HttpErrorResponse) => {
|
||||||
// Handle different HTTP error codes
|
|
||||||
if (error.status === 401) {
|
if (error.status === 401) {
|
||||||
// Unauthorized - redirect to login (but not for session check endpoint)
|
if (router.url !== '/login') {
|
||||||
// Skip redirect for /me endpoint to avoid issues during app initialization
|
|
||||||
if (!req.url.endsWith('/me')) {
|
|
||||||
console.error('Unauthorized access - redirecting to login');
|
|
||||||
router.navigate(['/login']);
|
router.navigate(['/login']);
|
||||||
}
|
}
|
||||||
} else if (error.status === 403) {
|
} else if (error.status === 403) {
|
||||||
// Forbidden
|
|
||||||
console.error('Access forbidden:', error.message);
|
console.error('Access forbidden:', error.message);
|
||||||
} else if (error.status === 0) {
|
|
||||||
// Network error
|
|
||||||
console.error('Network error - check if the server is running');
|
|
||||||
} else {
|
} else {
|
||||||
// Other errors
|
|
||||||
console.error(`HTTP Error ${error.status}:`, error.message);
|
console.error(`HTTP Error ${error.status}:`, error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-throw the error so components can handle it if needed
|
// Re-throw the error so components can handle it if needed
|
||||||
return throwError(() => error);
|
return throwError(() => error);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export interface ProjectFull {
|
||||||
|
|
||||||
export interface CreateProjectRequest {
|
export interface CreateProjectRequest {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProjectRequest {
|
export interface UpdateProjectRequest {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@
|
||||||
name="description"
|
name="description"
|
||||||
[(ngModel)]="description"
|
[(ngModel)]="description"
|
||||||
rows="4"
|
rows="4"
|
||||||
placeholder="Optional description"
|
placeholder="Enter a project description"
|
||||||
|
required
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { CreateProjectRequest, Project } from '../../models/projects.models';
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule],
|
imports: [CommonModule, FormsModule],
|
||||||
templateUrl: './project-create.component.html',
|
templateUrl: './project-create.component.html',
|
||||||
styleUrl: './project-create.component.css'
|
styleUrl: './project-create.component.css',
|
||||||
})
|
})
|
||||||
export class ProjectCreateComponent {
|
export class ProjectCreateComponent {
|
||||||
private apiService = inject(ApiService);
|
private apiService = inject(ApiService);
|
||||||
|
|
@ -27,9 +27,14 @@ export class ProjectCreateComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.description.trim()) {
|
||||||
|
this.errorMessage.set('Project description is required.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload: CreateProjectRequest = {
|
const payload: CreateProjectRequest = {
|
||||||
name: this.name.trim(),
|
name: this.name.trim(),
|
||||||
description: this.description.trim() || undefined
|
description: this.description.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isSaving.set(true);
|
this.isSaving.set(true);
|
||||||
|
|
@ -39,7 +44,7 @@ export class ProjectCreateComponent {
|
||||||
next: (project) => {
|
next: (project) => {
|
||||||
this.isSaving.set(false);
|
this.isSaving.set(false);
|
||||||
if (project?.id != null) {
|
if (project?.id != null) {
|
||||||
this.router.navigate(['/projects', project.id]);
|
this.router.navigate(['/projects/', project.id]);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
@ -47,9 +52,9 @@ export class ProjectCreateComponent {
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
this.isSaving.set(false);
|
this.isSaving.set(false);
|
||||||
this.errorMessage.set(
|
this.errorMessage.set(
|
||||||
error?.error?.message || 'Failed to create project. Please try again.'
|
error?.error?.message || 'Failed to create project. Please try again.',
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Injectable, inject, signal, computed } from '@angular/core';
|
import { Injectable, inject, signal, computed } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable, tap } from 'rxjs';
|
import { Observable, tap, catchError, throwError } from 'rxjs';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoginRequest, LoginResponse, User, AuthState } from '../models/auth.models';
|
import { LoginRequest, LoginResponse, User, AuthState } from '../models/auth.models';
|
||||||
import { environment } from '../config/environment';
|
import { environment } from '../config/environment';
|
||||||
|
|
@ -36,11 +36,11 @@ export class AuthService {
|
||||||
credentials
|
credentials
|
||||||
).pipe(
|
).pipe(
|
||||||
tap(response => {
|
tap(response => {
|
||||||
if (response.success && response.user) {
|
if (response.success || response.user) {
|
||||||
// Update auth state
|
// Update auth state even if the backend only sets a cookie
|
||||||
this.authState.set({
|
this.authState.set({
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
user: response.user
|
user: response.user ?? null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -52,7 +52,7 @@ export class AuthService {
|
||||||
* Clears the session cookie on the backend
|
* Clears the session cookie on the backend
|
||||||
*/
|
*/
|
||||||
logout(): Observable<any> {
|
logout(): Observable<any> {
|
||||||
return this.http.post(`${environment.apiBaseUrl}/me/logout`, {}).pipe(
|
return this.http.get(`${environment.apiBaseUrl}/me/logout`).pipe(
|
||||||
tap(() => {
|
tap(() => {
|
||||||
// Clear auth state
|
// Clear auth state
|
||||||
this.authState.set({
|
this.authState.set({
|
||||||
|
|
@ -61,6 +61,11 @@ export class AuthService {
|
||||||
});
|
});
|
||||||
// Redirect to login
|
// Redirect to login
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
// Even if logout fails on backend, clear local state
|
||||||
|
this.clearAuthState();
|
||||||
|
return throwError(() => error);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -88,5 +93,6 @@ export class AuthService {
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
user: null
|
user: null
|
||||||
});
|
});
|
||||||
|
this.router.navigate(['/login']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue