// 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 <= 5 * 60 * 1000); } return of(true); } checkTokenAndRedirect(): void { this.isTokenExpired().subscribe((isExpired) => { if (isExpired) { this.authService.doLogout().then( () => { this.router.navigate(["/login"]); }, (err) => { console.log(err); } ); this.router.navigate(["/login"]); } else { console.log("Token is valid, continuing..."); } }); } startTokenCheck(): void { timer(0, 2 * 60 * 1000) // Check every 5 minutes .pipe( switchMap(() => this.isTokenExpired()) ) .subscribe((isExpired) => { if (isExpired) { this.activity$.subscribe(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)); }); }); } }