Adapted some config for the api

This commit is contained in:
Marta Borgia Leiva 2026-02-09 22:46:16 +01:00
parent 773fa7ad6d
commit d1e016b7df
Signed by: a-mayb3
GPG key ID: 293AAC4FED165CE3
9 changed files with 74 additions and 54 deletions

View file

@ -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 { provideHttpClient, withInterceptors } from '@angular/common/http';
import { catchError, of } from 'rxjs';
import { catchError, of, throwError } from 'rxjs';
import { routes } from './app.routes';
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
*/
function initializeAuth(authService: AuthService) {
return () => authService.checkSession().pipe(
catchError((error) => {
// Session check failed - user is not logged in or session expired
console.log('No active session or session expired');
authService.clearAuthState();
return of(null);
})
);
return () =>
authService.checkSession().pipe(
catchError((error) => {
console.error('Session check failed:', error);
if (error?.status === 401 || error?.status === 422) {
authService.clearAuthState();
return of(null);
}
authService.clearAuthState();
return throwError(() => error);
}),
);
}
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes),
provideHttpClient(
withInterceptors([httpInterceptor])
),
provideHttpClient(withInterceptors([httpInterceptor])),
{
provide: APP_INITIALIZER,
useFactory: initializeAuth,
deps: [AuthService],
multi: true
}
]
multi: true,
},
],
};

View file

@ -1,8 +1,5 @@
<app-navbar />
<main class="main"></main>
<main class="main">
<router-outlet />
</main>
<app-footer />
<router-outlet />

View file

@ -4,15 +4,28 @@ import { LoginComponent } from './pages/login/login.component';
export const routes: Routes = [
// Public routes
{
path: 'login',
component: LoginComponent
{
path: 'login',
component: LoginComponent
},
// Protected routes - require authentication
{
path: '',
{
path: '',
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)
}
]
}
];

View file

@ -2,11 +2,12 @@ import { Component, signal, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { AuthService } from './services/auth.service';
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({
selector: 'app-root',
imports: [RouterOutlet, CommonModule, Navbar],
imports: [RouterOutlet, CommonModule, NavbarComponent, FooterComponent],
templateUrl: './app.html',
styleUrl: './app.css'
})

View file

@ -15,33 +15,24 @@ export const httpInterceptor: HttpInterceptorFn = (req, next) => {
// Clone the request to add withCredentials flag
// This ensures cookies are sent with every request
const reqWithCredentials = req.clone({
withCredentials: true
withCredentials: true,
});
// Pass the cloned request to the next handler
return next(reqWithCredentials).pipe(
catchError((error: HttpErrorResponse) => {
// Handle different HTTP error codes
if (error.status === 401) {
// Unauthorized - redirect to login (but not for session check endpoint)
// Skip redirect for /me endpoint to avoid issues during app initialization
if (!req.url.endsWith('/me')) {
console.error('Unauthorized access - redirecting to login');
if (router.url !== '/login') {
router.navigate(['/login']);
}
} else if (error.status === 403) {
// Forbidden
console.error('Access forbidden:', error.message);
} else if (error.status === 0) {
// Network error
console.error('Network error - check if the server is running');
} else {
// Other errors
console.error(`HTTP Error ${error.status}:`, error.message);
}
// Re-throw the error so components can handle it if needed
return throwError(() => error);
})
}),
);
};

View file

@ -17,7 +17,7 @@ export interface ProjectFull {
export interface CreateProjectRequest {
name: string;
description?: string;
description: string;
}
export interface UpdateProjectRequest {

View file

@ -26,7 +26,8 @@
name="description"
[(ngModel)]="description"
rows="4"
placeholder="Optional description"
placeholder="Enter a project description"
required
></textarea>
</div>

View file

@ -10,7 +10,7 @@ import { CreateProjectRequest, Project } from '../../models/projects.models';
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './project-create.component.html',
styleUrl: './project-create.component.css'
styleUrl: './project-create.component.css',
})
export class ProjectCreateComponent {
private apiService = inject(ApiService);
@ -27,9 +27,14 @@ export class ProjectCreateComponent {
return;
}
if (!this.description.trim()) {
this.errorMessage.set('Project description is required.');
return;
}
const payload: CreateProjectRequest = {
name: this.name.trim(),
description: this.description.trim() || undefined
description: this.description.trim(),
};
this.isSaving.set(true);
@ -39,7 +44,7 @@ export class ProjectCreateComponent {
next: (project) => {
this.isSaving.set(false);
if (project?.id != null) {
this.router.navigate(['/projects', project.id]);
this.router.navigate(['/projects/', project.id]);
} else {
this.router.navigate(['/']);
}
@ -47,9 +52,9 @@ export class ProjectCreateComponent {
error: (error) => {
this.isSaving.set(false);
this.errorMessage.set(
error?.error?.message || 'Failed to create project. Please try again.'
error?.error?.message || 'Failed to create project. Please try again.',
);
}
},
});
}

View file

@ -1,6 +1,6 @@
import { Injectable, inject, signal, computed } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, tap } from 'rxjs';
import { Observable, tap, catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';
import { LoginRequest, LoginResponse, User, AuthState } from '../models/auth.models';
import { environment } from '../config/environment';
@ -36,11 +36,11 @@ export class AuthService {
credentials
).pipe(
tap(response => {
if (response.success && response.user) {
// Update auth state
if (response.success || response.user) {
// Update auth state even if the backend only sets a cookie
this.authState.set({
isAuthenticated: true,
user: response.user
user: response.user ?? null
});
}
})
@ -52,7 +52,7 @@ export class AuthService {
* Clears the session cookie on the backend
*/
logout(): Observable<any> {
return this.http.post(`${environment.apiBaseUrl}/me/logout`, {}).pipe(
return this.http.get(`${environment.apiBaseUrl}/me/logout`).pipe(
tap(() => {
// Clear auth state
this.authState.set({
@ -61,6 +61,11 @@ export class AuthService {
});
// Redirect to 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,
user: null
});
this.router.navigate(['/login']);
}
}