feat: login and register, style is not ready
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
export type AuthDialogState = 'login' | 'register' | null;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthDialogService {
|
||||
private readonly dialogStateSubject = new BehaviorSubject<AuthDialogState>(null);
|
||||
|
||||
readonly dialogState$ = this.dialogStateSubject.asObservable();
|
||||
|
||||
openLogin(): void {
|
||||
this.dialogStateSubject.next('login');
|
||||
}
|
||||
|
||||
openRegister(): void {
|
||||
this.dialogStateSubject.next('register');
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.dialogStateSubject.next(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { HttpInterceptorFn } from '@angular/common/http';
|
||||
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
// Add token to protected endpoints only (not registration or login)
|
||||
const isProtectedEndpoint =
|
||||
req.url.includes('/api/account/me') ||
|
||||
req.url.includes('/api/account/bots') ||
|
||||
req.url.includes('/api/account/official-bots') ||
|
||||
req.url.includes('/api/challenge');
|
||||
|
||||
if (token && isProtectedEndpoint) {
|
||||
req = req.clone({
|
||||
setHeaders: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return next(req);
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { LoginRequest, RegisterRequest, RegisterResponse, LoginResponse, CurrentUser } from '../models/auth.models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthService {
|
||||
private readonly apiBase = environment.apiBaseUrl;
|
||||
private readonly accountServiceUrl = environment.accountServiceUrl;
|
||||
private readonly http = inject(HttpClient);
|
||||
|
||||
private currentUserSubject = new BehaviorSubject<CurrentUser | null>(null);
|
||||
public currentUser$ = this.currentUserSubject.asObservable();
|
||||
|
||||
constructor() {
|
||||
this.loadCurrentUser();
|
||||
}
|
||||
|
||||
login(username: string, password: string): Observable<LoginResponse> {
|
||||
return this.http
|
||||
.post<LoginResponse>(`${this.accountServiceUrl}/api/account/login`, {
|
||||
username,
|
||||
password
|
||||
})
|
||||
.pipe(
|
||||
tap((response) => {
|
||||
localStorage.setItem('token', response.token);
|
||||
localStorage.setItem('username', username);
|
||||
// After login, fetch current user info
|
||||
this.getCurrentUser().subscribe();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
register(username: string, password: string, email?: string): Observable<RegisterResponse> {
|
||||
return this.http
|
||||
.post<RegisterResponse>(`${this.accountServiceUrl}/api/account`, {
|
||||
username,
|
||||
password,
|
||||
email
|
||||
})
|
||||
.pipe(
|
||||
tap((response) => {
|
||||
localStorage.setItem('username', response.username);
|
||||
localStorage.setItem('userId', response.id);
|
||||
this.currentUserSubject.next({
|
||||
id: response.id,
|
||||
username: response.username,
|
||||
rating: response.rating,
|
||||
createdAt: response.createdAt
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getCurrentUser(): Observable<CurrentUser> {
|
||||
return this.http.get<CurrentUser>(`${this.accountServiceUrl}/api/account/me`).pipe(
|
||||
tap((user) => {
|
||||
localStorage.setItem('username', user.username);
|
||||
localStorage.setItem('userId', user.id);
|
||||
this.currentUserSubject.next(user);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('userId');
|
||||
this.currentUserSubject.next(null);
|
||||
}
|
||||
|
||||
isLoggedIn(): boolean {
|
||||
return !!localStorage.getItem('token');
|
||||
}
|
||||
|
||||
private loadCurrentUser(): void {
|
||||
const token = localStorage.getItem('token');
|
||||
const username = localStorage.getItem('username');
|
||||
const userId = localStorage.getItem('userId');
|
||||
if (token && username && userId) {
|
||||
// Try to verify token is still valid by fetching current user
|
||||
this.getCurrentUser().subscribe({
|
||||
error: () => {
|
||||
// Token is invalid, clear it
|
||||
this.logout();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ThemeService {
|
||||
private readonly darkModeSubject = new BehaviorSubject<boolean>(false);
|
||||
|
||||
readonly darkMode$ = this.darkModeSubject.asObservable();
|
||||
|
||||
initTheme(): void {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
this.applyDarkMode(savedTheme === 'dark');
|
||||
}
|
||||
|
||||
toggleTheme(): void {
|
||||
this.applyDarkMode(!this.darkModeSubject.value);
|
||||
}
|
||||
|
||||
setDarkMode(isDarkMode: boolean): void {
|
||||
this.applyDarkMode(isDarkMode);
|
||||
}
|
||||
|
||||
private applyDarkMode(isDarkMode: boolean): void {
|
||||
if (isDarkMode) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
localStorage.removeItem('theme');
|
||||
}
|
||||
|
||||
this.darkModeSubject.next(isDarkMode);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user