AI Implementation feature(842): First-Start Create Admin Modal 1.01 (#4)
This commit was merged in pull request #4.
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
import { Injectable, signal, computed } from '@angular/core';
|
||||
import { Injectable, signal, computed, inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import {
|
||||
readStoredSession,
|
||||
writeStoredSession,
|
||||
clearStoredSession,
|
||||
StoredSession,
|
||||
} from './auth.session-storage';
|
||||
|
||||
export interface CurrentUser {
|
||||
id: string;
|
||||
@@ -6,10 +14,24 @@ export interface CurrentUser {
|
||||
role: 'admin' | 'player';
|
||||
}
|
||||
|
||||
interface RefreshResponse {
|
||||
accessToken: string;
|
||||
expiresIn: number;
|
||||
user: CurrentUser;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthService {
|
||||
private http: HttpClient;
|
||||
|
||||
constructor(http?: HttpClient) {
|
||||
this.http = http ?? inject(HttpClient);
|
||||
}
|
||||
|
||||
private accessToken = signal<string | null>(null);
|
||||
private user = signal<CurrentUser | null>(null);
|
||||
readonly hydrated = signal<boolean>(false);
|
||||
private hydrateWaiters: Array<() => void> = [];
|
||||
|
||||
readonly currentUser = computed(() => this.user());
|
||||
readonly isAuthenticated = computed(() => !!this.user());
|
||||
@@ -17,14 +39,69 @@ export class AuthService {
|
||||
setSession(token: string, user: CurrentUser): void {
|
||||
this.accessToken.set(token);
|
||||
this.user.set(user);
|
||||
writeStoredSession(this.sessionStorage(), { token, user });
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.accessToken.set(null);
|
||||
this.user.set(null);
|
||||
clearStoredSession(this.sessionStorage());
|
||||
}
|
||||
|
||||
getAccessToken(): string | null {
|
||||
return this.accessToken();
|
||||
}
|
||||
|
||||
private sessionStorage(): Storage {
|
||||
return typeof window !== 'undefined' ? window.sessionStorage : (undefined as unknown as Storage);
|
||||
}
|
||||
|
||||
private restoreFromStorage(): void {
|
||||
const stored = readStoredSession(this.sessionStorage());
|
||||
if (stored) {
|
||||
this.accessToken.set(stored.token);
|
||||
this.user.set(stored.user as CurrentUser);
|
||||
}
|
||||
}
|
||||
|
||||
private markHydrated(): void {
|
||||
if (this.hydrated()) return;
|
||||
this.hydrated.set(true);
|
||||
const waiters = this.hydrateWaiters;
|
||||
this.hydrateWaiters = [];
|
||||
for (const resolve of waiters) resolve();
|
||||
}
|
||||
|
||||
waitUntilHydrated(): Promise<void> {
|
||||
if (this.hydrated()) return Promise.resolve();
|
||||
return new Promise<void>((resolve) => {
|
||||
this.hydrateWaiters.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
async restoreSession(): Promise<boolean> {
|
||||
this.restoreFromStorage();
|
||||
|
||||
try {
|
||||
const res = await firstValueFrom(
|
||||
this.http.post<RefreshResponse>(
|
||||
'/api/v1/auth/refresh',
|
||||
{},
|
||||
{ withCredentials: true },
|
||||
),
|
||||
);
|
||||
if (res && res.accessToken && res.user) {
|
||||
this.setSession(res.accessToken, res.user);
|
||||
this.markHydrated();
|
||||
return true;
|
||||
}
|
||||
this.clear();
|
||||
this.markHydrated();
|
||||
return false;
|
||||
} catch {
|
||||
this.clear();
|
||||
this.markHydrated();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user