Admin mode. Data driven content sections. Bugfixes.

This commit is contained in:
2026-06-19 17:36:35 +00:00
parent bd65455945
commit 266c2451f1
30 changed files with 1733 additions and 13 deletions
+95
View File
@@ -0,0 +1,95 @@
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, firstValueFrom, map, Observable, tap } from 'rxjs';
import { environment } from '../../environments/environment';
import { AppUser, AuthTokenResponse } from '../interfaces/auth';
const TOKEN_KEY = 'kmtn_access_token';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private http = inject(HttpClient);
private baseUrl = `${environment.apiBaseUrl}/api/auth`;
private authStateSubject = new BehaviorSubject<AppUser | null>(null);
/** Observable that emits the current auth state. */
readonly authState$: Observable<AppUser | null> = this.authStateSubject.asObservable();
constructor() {
// Check for existing token on init
this._initAuth();
}
/** Login with username/password (bootstrap admin). */
login(username: string, password: string): Observable<AuthTokenResponse> {
return this.http.post<AuthTokenResponse>(`${this.baseUrl}/login`, {
username,
password,
}).pipe(
tap((resp) => localStorage.setItem(TOKEN_KEY, resp.access_token)),
);
}
/** Login with Google ID token. */
loginWithGoogle(idToken: string): Observable<AuthTokenResponse> {
return this.http.post<AuthTokenResponse>(`${this.baseUrl}/google`, {
id_token: idToken,
}).pipe(
tap((resp) => localStorage.setItem(TOKEN_KEY, resp.access_token)),
);
}
/** Clear stored token and reset auth state. */
logout(): void {
localStorage.removeItem(TOKEN_KEY);
this.authStateSubject.next(null);
}
/** Fetch current user info from the API. */
getCurrentUser(): Promise<AppUser | null> {
const token = this._getToken();
if (!token) return Promise.resolve(null);
return firstValueFrom(
this.http.get<AppUser>(`${this.baseUrl}/me`).pipe(
tap((user) => this.authStateSubject.next(user)),
map((user) => user),
)
).catch(() => {
this.logout();
return null;
});
}
/** Check if there is a stored token. */
isLoggedIn(): boolean {
return !!this._getToken();
}
/** Check if the current user is an admin (synchronous, based on cached state). */
isAdmin(): boolean {
return this.authStateSubject.value?.is_admin ?? false;
}
/** Get the current user synchronously (from cached state). */
getUser(): AppUser | null {
return this.authStateSubject.value;
}
// ── Private ──────────────────────────────────────────────
private _getToken(): string | null {
return localStorage.getItem(TOKEN_KEY);
}
private async _initAuth(): Promise<void> {
const token = this._getToken();
if (token) {
await this.getCurrentUser();
}
}
}