// src/app/services/login.service.ts import { Injectable, NgZone } from "@angular/core"; import { HttpClient, HttpHeaders } from "@angular/common/http"; import { BehaviorSubject, Observable, of, Subject, timer, fromEvent, merge, } from "rxjs"; import { jwtDecode } from "jwt-decode"; import { Router } from "@angular/router"; import { switchMap, debounceTime, mapTo, startWith } from "rxjs/operators"; import { AuthService } from "src/app/_services/auth.service"; const BASE_URL = "https://kapi.absys.ninja/hemat"; interface CustomJwtPayload { exp: number; scope: string; iat: number; preferred_username: string; name: string; email: string; family_name: string; given_name: string; sub: string; } @Injectable({ providedIn: "root", }) export class LoginService { private readonly tokenKey = "currentUser"; private tabSelected = new BehaviorSubject(null); public _tabSelected = this.tabSelected.asObservable(); private activitySubject = new Subject(); public activity$: Observable = this.activitySubject.asObservable(); currentUser: any; constructor( private http: HttpClient, private router: Router, private authService: AuthService, private ngZone: NgZone ) { this.startTrackingActivity(); this.startTokenCheck(); this.currentUser = JSON.parse(localStorage.getItem("currentUser")); } setTabsSelected(e: string) { this.tabSelected.next(e); } updatePassword(data: any): Observable { const endpoint = `/users`; const url = `${BASE_URL}${endpoint}/reset-password`; const headers = new HttpHeaders({ "Content-Type": "application/json", "x-api-key": "j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT", }); return this.http.put(url, data, { headers }); } getDataProfil(id: string): Observable { const endpoint = `/users`; const url = `${BASE_URL}${endpoint}/byUserid/${id}`; const headers = new HttpHeaders({ "Content-Type": "application/json", "x-api-key": "j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT", }); return this.http.get(url, { headers }); } updateProfile(data: any, userId: string): Observable { const endpoint = `/users`; const url = `${BASE_URL}${endpoint}/update/${userId}`; const headers = new HttpHeaders({ "x-api-key": "j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT", }); return this.http.post(`${url}`, data, { headers }); } updateUserProfile(data: any): Observable { const body = { refresh_token: data }; const endpoint = `/users`; const url = `${BASE_URL}${endpoint}/refresh-token`; const headers = new HttpHeaders({ "x-api-key": "j2yaYvPSQcsEEmHh3NEobfiXyyXmmnHT", }); return this.http.post(`${url}`, body, { headers }); } isTokenExpired(): Observable { const tokenData = localStorage.getItem(this.tokenKey); if (tokenData) { const tokenInfo = JSON.parse(tokenData); const decodedToken = jwtDecode(tokenInfo.refresh_token); const expiryDate = decodedToken.exp * 1000; const now = new Date().getTime(); const timeLeft = expiryDate - now; return of(timeLeft <= 2 * 60 * 1000); } return of(true); } checkTokenAndRedirect(): void { this.isTokenExpired().subscribe((isExpired) => { if (isExpired) { this.authService.doLogout().then( () => { this.router.navigate(["/login"]); window.location.reload(); }, (err) => { console.log(err); } ); this.router.navigate(["/login"]); window.location.reload(); } else { console.log("Token is valid, continuing..."); } }); } startTokenCheck(): void { timer(0, 2 * 60 * 1000) // Check every 5 minutes .pipe(switchMap(() => this.isTokenExpired())) .subscribe((isExpired) => { // console.log(isExpired); if (isExpired) { this.activity$.subscribe((isActive) => { // console.log(isActive); if (!isActive) { this.checkTokenAndRedirect(); } else { console.log( "Token expired but user is active. Refresh token not implemented." ); this.updateUserProfile(this.currentUser.refresh_token).subscribe( (resp) => { const decodedToken = jwtDecode( resp.access_token ); localStorage.setItem( "account_info", JSON.stringify(decodedToken) ); const userProfile = { access_token: resp.access_token, refresh_token: resp.refresh_token, displayName: decodedToken.name, buildingId: 4, }; localStorage.setItem( "currentUser", JSON.stringify(userProfile) ); window.location.reload(); } ); } }); } }); } startTrackingActivity(): void { this.ngZone.runOutsideAngular(() => { const activityEvents$ = merge( fromEvent(window, "mousemove"), fromEvent(window, "click"), fromEvent(window, "keypress"), fromEvent(window, "scroll") ); activityEvents$ .pipe( startWith(null), switchMap(() => merge( timer(0).pipe(mapTo(true)), timer(5 * 60 * 1000).pipe(mapTo(false)) // 5 minutes of inactivity ) ), debounceTime(300) ) .subscribe((active) => { this.ngZone.run(() => this.activitySubject.next(active)); }); }); } }