diff --git a/src/app/config/environment.ts b/src/app/config/environment.ts new file mode 100644 index 0000000..1886cec --- /dev/null +++ b/src/app/config/environment.ts @@ -0,0 +1,8 @@ +/** + * Environment configuration + * Update these values based on your environment (development, production, etc.) + */ +export const environment = { + production: false, + apiBaseUrl: 'http://localhost:8000', +}; diff --git a/src/app/guards/auth.guard.ts b/src/app/guards/auth.guard.ts new file mode 100644 index 0000000..ce39f68 --- /dev/null +++ b/src/app/guards/auth.guard.ts @@ -0,0 +1,19 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router, UrlTree } from '@angular/router'; +import { AuthService } from '../services/auth.service'; + +/** + * Auth guard to protect routes that require authentication + * Usage: Add to route definition with canActivate: [authGuard] + */ +export const authGuard: CanActivateFn = (): boolean | UrlTree => { + const authService = inject(AuthService); + const router = inject(Router); + + if (authService.isAuthenticated()) { + return true; + } else { + // Redirect to login page + return router.createUrlTree(['/login']); + } +}; diff --git a/src/app/models/auth.models.ts b/src/app/models/auth.models.ts new file mode 100644 index 0000000..dc2bdc7 --- /dev/null +++ b/src/app/models/auth.models.ts @@ -0,0 +1,30 @@ +/** + * Authentication-related type definitions + */ + +import { Project } from "./projects.models"; + +export interface LoginRequest { + email: string; + password: string; +} + +export interface LoginResponse { + success: boolean; + message?: string; + user?: User; + token?: string; // Optional: if you need the JWT on client side +} + +export interface User { + id: string | number; + name: string; + email: string; + projects?: Project[]; // Add project type if available + // Add other user properties as needed +} + +export interface AuthState { + isAuthenticated: boolean; + user: User | null; +} diff --git a/src/app/models/projects.models.ts b/src/app/models/projects.models.ts new file mode 100644 index 0000000..2111465 --- /dev/null +++ b/src/app/models/projects.models.ts @@ -0,0 +1,26 @@ +import { User } from "./auth.models"; +import { Task } from "./tasks.models"; + +export interface Project { + id: number; + name: string; + description?: string; +} + +export interface ProjectFull { + id: number; + name: string; + description?: string; + tasks: Task[]; + users: User[]; +} + +export interface CreateProjectRequest { + name: string; + description?: string; +} + +export interface UpdateProjectRequest { + name?: string; + description?: string; +} \ No newline at end of file diff --git a/src/app/models/tasks.models.ts b/src/app/models/tasks.models.ts new file mode 100644 index 0000000..ecde556 --- /dev/null +++ b/src/app/models/tasks.models.ts @@ -0,0 +1,7 @@ + +export interface Task{ + id: number; + title: string; + description?: string; + status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'STASHED' | 'FAILED'; +} \ No newline at end of file diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts new file mode 100644 index 0000000..b0eaba8 --- /dev/null +++ b/src/app/services/api.service.ts @@ -0,0 +1,44 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { environment } from '../config/environment'; + +/** + * Base API service that provides common HTTP operations + * Other services can inject this for reusable API patterns + */ +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + private http = inject(HttpClient); + protected readonly baseUrl = environment.apiBaseUrl; + + /** + * HTTP GET request + */ + get(endpoint: string): Observable { + return this.http.get(`${this.baseUrl}${endpoint}`); + } + + /** + * HTTP POST request + */ + post(endpoint: string, body: any): Observable { + return this.http.post(`${this.baseUrl}${endpoint}`, body); + } + + /** + * HTTP PUT request + */ + put(endpoint: string, body: any): Observable { + return this.http.put(`${this.baseUrl}${endpoint}`, body); + } + + /** + * HTTP DELETE request + */ + delete(endpoint: string): Observable { + return this.http.delete(`${this.baseUrl}${endpoint}`); + } +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts new file mode 100644 index 0000000..18f3d00 --- /dev/null +++ b/src/app/services/auth.service.ts @@ -0,0 +1,92 @@ +import { Injectable, inject, signal, computed } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, tap } from 'rxjs'; +import { Router } from '@angular/router'; +import { LoginRequest, LoginResponse, User, AuthState } from '../models/auth.models'; +import { environment } from '../config/environment'; + +/** + * Authentication service that manages user login, logout, and session state + * Uses signals for reactive state management + */ +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + private http = inject(HttpClient); + private router = inject(Router); + + // Reactive auth state using signals + private authState = signal({ + isAuthenticated: false, + user: null + }); + + // Public computed signals for components to consume + readonly isAuthenticated = computed(() => this.authState().isAuthenticated); + readonly currentUser = computed(() => this.authState().user); + + /** + * Login with credentials + * The JWT will be set as an HTTP-only cookie by the backend + */ + login(credentials: LoginRequest): Observable { + return this.http.post( + `${environment.apiBaseUrl}/auth/login`, + credentials + ).pipe( + tap(response => { + if (response.success && response.user) { + // Update auth state + this.authState.set({ + isAuthenticated: true, + user: response.user + }); + } + }) + ); + } + + /** + * Logout the current user + * Clears the session cookie on the backend + */ + logout(): Observable { + return this.http.post(`${environment.apiBaseUrl}/me/logout`, {}).pipe( + tap(() => { + // Clear auth state + this.authState.set({ + isAuthenticated: false, + user: null + }); + // Redirect to login + this.router.navigate(['/login']); + }) + ); + } + + /** + * Check current session / get current user + * Call this on app initialization to restore session state + */ + checkSession(): Observable { + return this.http.get(`${environment.apiBaseUrl}/me`).pipe( + tap(user => { + this.authState.set({ + isAuthenticated: true, + user: user + }); + }) + ); + } + + /** + * Clear auth state (use when session expires or on error) + */ + clearAuthState(): void { + this.authState.set({ + isAuthenticated: false, + user: null + }); + } +}