import { computed, inject, Injectable, Signal, signal, WritableSignal } from "@angular/core"; import { RegisterUserRequest, User } from "../../../core/models/user.model"; import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { API_URL, BACKEND_URL } from "../../../core/tokens/api-url-tokens"; import { switchMap, tap } from "rxjs"; import { LocalStorageService } from "../../../core/services/local-storage.service"; export enum AuthState { Loading = "loading", Authenticated = "authenticated", Unauthenticated = "unauthenticated", } /** * UserService - manages user related operations * * ## Auth States * - loading: user is being fetched from the server * - authenticated: user is authenticated * - unauthenticated: user is not authenticated */ @Injectable({ providedIn: "root", }) export class AuthService { // User states readonly authState: WritableSignal; readonly user: WritableSignal; // Computed state for easy checking readonly isAuthenticated: Signal; // Dependent services private localStorage = inject(LocalStorageService); private http: HttpClient = inject(HttpClient); // Constants private readonly userKey = "ekart_user"; private apiUrl = inject(API_URL); private backendURL = inject(BACKEND_URL); constructor() { const cachedUser = this.localStorage.getItem(this.userKey); this.authState = signal( cachedUser ? AuthState.Authenticated : AuthState.Unauthenticated, ); this.user = signal(cachedUser); this.isAuthenticated = computed(() => !!this.user()); } register(userRequest: RegisterUserRequest) { return this.http.post(`${this.apiUrl}/register`, userRequest); } /** * Laravel API expects the csrf cookie to be set before making a request. * First set the cookie then attempt to login. * If the login is successful, set the user in the state. */ login(credentials: { email: string; password: string }) { return this.getCsrfCookie().pipe( switchMap(() => this.http.post(`${this.backendURL}/login`, credentials, { observe: "response" }), ), switchMap(() => this.getCurrentUser()), ); } getCurrentUser() { return this.http.get(`${this.apiUrl}/user`).pipe( tap({ next: (user) => this.setAuth(user), error: (error: HttpErrorResponse) => this.purgeAuth(), }), ); } logout() { return this.http.post(`${this.backendURL}/logout`, {}).pipe( tap({ next: () => this.purgeAuth(), }), ); } private getCsrfCookie() { return this.http.get(`${this.backendURL}/sanctum/csrf-cookie`); } private setAuth(user: User) { this.localStorage.setItem(this.userKey, user); this.user.set(user); this.authState.set(AuthState.Authenticated); } private purgeAuth() { this.localStorage.removeItem(this.userKey); this.user.set(null); this.authState.set(AuthState.Unauthenticated); } }