AI Implementation feature(857): Authenticated Shell: Header, Quick Tabs and Change Password (#14)
This commit was merged in pull request #14.
This commit is contained in:
@@ -12,6 +12,8 @@ export interface CurrentUser {
|
||||
id: string;
|
||||
username: string;
|
||||
role: 'admin' | 'player';
|
||||
rank?: number | null;
|
||||
points?: number;
|
||||
}
|
||||
|
||||
interface RefreshResponse {
|
||||
@@ -20,6 +22,14 @@ interface RefreshResponse {
|
||||
user: CurrentUser;
|
||||
}
|
||||
|
||||
export interface MeResponse {
|
||||
id: string;
|
||||
username: string;
|
||||
role: 'admin' | 'player';
|
||||
rank: number | null;
|
||||
points: number;
|
||||
}
|
||||
|
||||
export interface LoginSuccess {
|
||||
ok: true;
|
||||
accessToken: string;
|
||||
@@ -52,6 +62,19 @@ export interface RegisterFailure {
|
||||
|
||||
export type RegisterResult = RegisterSuccess | RegisterFailure;
|
||||
|
||||
export interface ChangePasswordSuccess {
|
||||
ok: true;
|
||||
}
|
||||
|
||||
export interface ChangePasswordFailure {
|
||||
ok: false;
|
||||
status: number;
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ChangePasswordResult = ChangePasswordSuccess | ChangePasswordFailure;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthService {
|
||||
private http: HttpClient;
|
||||
@@ -126,6 +149,49 @@ export class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.ensureCsrf();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this.http.post('/api/v1/auth/logout', {}, { withCredentials: true, observe: 'response' }),
|
||||
);
|
||||
} catch {
|
||||
// ignore network/auth errors; we still clear local state
|
||||
}
|
||||
this.clear();
|
||||
}
|
||||
|
||||
async me(): Promise<MeResponse> {
|
||||
const res = await firstValueFrom(
|
||||
this.http.get<MeResponse>('/api/v1/auth/me', { withCredentials: true }),
|
||||
);
|
||||
const current = this.user();
|
||||
if (current && current.id === res.id) {
|
||||
this.user.set({ ...current, rank: res.rank, points: res.points });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async changePassword(dto: {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
confirmNewPassword: string;
|
||||
}): Promise<ChangePasswordResult> {
|
||||
await this.ensureCsrf();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this.http.post(
|
||||
'/api/v1/auth/change-password',
|
||||
dto,
|
||||
{ withCredentials: true, observe: 'response' },
|
||||
),
|
||||
);
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
return mapAuthError(e as HttpErrorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
setSession(token: string, user: CurrentUser): void {
|
||||
this.accessToken.set(token);
|
||||
this.user.set(user);
|
||||
@@ -204,4 +270,4 @@ function mapAuthError(err: HttpErrorResponse): LoginFailure {
|
||||
code: body?.code ?? 'INTERNAL',
|
||||
message: body?.message ?? err.message ?? 'Authentication failed',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user