|
|
import { Injectable } from '@angular/core'; |
|
|
import { HttpClient } from '@angular/common/http'; |
|
|
import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; |
|
|
import { tap, catchError } from 'rxjs/operators'; |
|
|
import { Router } from '@angular/router'; |
|
|
|
|
|
|
|
|
|
|
|
@Injectable({ |
|
|
providedIn: 'root' |
|
|
}) |
|
|
|
|
|
export class AuthService { |
|
|
private apiUrl = location.hostname.endsWith('hf.space') |
|
|
? 'https://pykara-py-learn-backend.hf.space' |
|
|
: 'http://localhost:5000'; |
|
|
private loggedInSubject = new BehaviorSubject<boolean>(false); |
|
|
public isLoggedIn$ = this.loggedInSubject.asObservable(); |
|
|
private refreshIntervalId: any; |
|
|
constructor(private http: HttpClient, private router: Router) { } |
|
|
|
|
|
|
|
|
isLoggedIn(): boolean { |
|
|
return this.loggedInSubject.value; |
|
|
} |
|
|
|
|
|
|
|
|
setLoggedIn(status: boolean): void { |
|
|
this.loggedInSubject.next(status); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
login(username: string, password: string): Observable<any> { |
|
|
return this.http.post(`${this.apiUrl}/login`, { username, password }, { withCredentials: true }); |
|
|
} |
|
|
|
|
|
|
|
|
startAutoRefresh(): void { |
|
|
if (this.refreshIntervalId) return; |
|
|
this.refreshIntervalId = setInterval(() => { |
|
|
this.refreshAccessToken(); |
|
|
}, 12 * 60 * 1000); |
|
|
} |
|
|
|
|
|
clearAutoRefresh(): void { |
|
|
clearInterval(this.refreshIntervalId); |
|
|
this.refreshIntervalId = null; |
|
|
} |
|
|
|
|
|
refreshAccessToken(): void { |
|
|
this.http.post(`${this.apiUrl}/refresh`, {}, { withCredentials: true }).subscribe( |
|
|
(response: any) => { |
|
|
console.log("β
Access token refreshed:", response.access_token); |
|
|
|
|
|
}, |
|
|
(error) => { |
|
|
console.error("β Refresh failed:", error); |
|
|
|
|
|
if ( |
|
|
error.status === 401 && |
|
|
error.error && |
|
|
(error.error.message === 'Refresh token has expired' || error.error.message === 'Invalid refresh token') |
|
|
) { |
|
|
|
|
|
|
|
|
|
|
|
this.clearTokens(); |
|
|
|
|
|
|
|
|
this.router.navigate(['/auth']); |
|
|
} |
|
|
} |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logout(): Observable<any> { |
|
|
console.log("π§ Sending logout request with credentials"); |
|
|
|
|
|
return this.http.post(`${this.apiUrl}/logout`, {}, { withCredentials: true }).pipe( |
|
|
tap(response => { |
|
|
console.log('π Response from backend:', response); |
|
|
this.clearTokens(); |
|
|
this.clearAutoRefresh(); |
|
|
this.setLoggedIn(false); |
|
|
}), |
|
|
catchError(error => { |
|
|
console.error('β Error from backend:', error); |
|
|
|
|
|
this.clearTokens(); |
|
|
this.clearAutoRefresh(); |
|
|
this.setLoggedIn(false); |
|
|
return throwError(() => error); |
|
|
}) |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clearTokens(): void { |
|
|
document.cookie = 'access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; |
|
|
document.cookie = 'refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; |
|
|
this.clearAutoRefresh(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getAccessToken(): string | null { |
|
|
const cookies = document.cookie.split('; '); |
|
|
for (let cookie of cookies) { |
|
|
if (cookie.startsWith('access_token=')) { |
|
|
return cookie.split('=')[1]; |
|
|
} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
saveTokens(accessToken: string, refreshToken: string): void { |
|
|
|
|
|
localStorage.setItem('access_token', accessToken); |
|
|
localStorage.setItem('refresh_token', refreshToken); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkSession(): Observable<boolean> { |
|
|
return this.http.get(`${this.apiUrl}/check-auth`, { withCredentials: true }).pipe( |
|
|
tap((res: any) => { |
|
|
console.log('β
Session valid:', res); |
|
|
this.setLoggedIn(true); |
|
|
this.startAutoRefresh(); |
|
|
return true; |
|
|
}), |
|
|
catchError((err) => { |
|
|
if (err.status === 401) { |
|
|
|
|
|
console.warn('π Access token expired. Trying to refresh...'); |
|
|
|
|
|
return this.http.post(`${this.apiUrl}/refresh`, {}, { withCredentials: true }).pipe( |
|
|
tap((refreshRes: any) => { |
|
|
console.log("β
Token refreshed during checkSession."); |
|
|
|
|
|
this.setLoggedIn(true); |
|
|
this.startAutoRefresh(); |
|
|
}), |
|
|
catchError((refreshErr) => { |
|
|
console.error("β Refresh token failed during checkSession.", refreshErr); |
|
|
this.setLoggedIn(false); |
|
|
return of(false); |
|
|
}) |
|
|
); |
|
|
} else { |
|
|
console.error("β Unknown error during checkSession", err); |
|
|
this.setLoggedIn(false); |
|
|
return of(false); |
|
|
} |
|
|
}) |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|