humainflow-web/src/app/services/authorization/authorization-call.ts

100 lines
3.8 KiB
TypeScript

import { AuthorizationCallServiceBase } from "./authorization-call.base";
import {
ChangePasswordRequest,
User,
UserRegistration,
UserRole
} from "@models/user";
import { catchError, map, Observable, of, throwError } from "rxjs";
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { inject } from "@angular/core";
import { environment } from "@environment";
import { extractHttpErrorMessage, toHttpError } from "@services/shared/http-error.util";
export class AuthorizationCallService extends AuthorizationCallServiceBase {
private readonly http = inject(HttpClient);
override login(username: string, password: string): Observable<User> {
return this.http
.post<unknown>(`${environment.apiUrl}/auth/login`, { username, password })
.pipe(
map((raw) => this.userFromApi(raw, username)),
catchError((error: unknown) => {
if (error instanceof HttpErrorResponse && error.status === 404) {
return throwError(() => new Error('User not found'));
}
if (error instanceof HttpErrorResponse && error.status === 401) {
return throwError(() => new Error('Invalid password'));
}
return throwError(() => error);
})
);
}
override currentUser(): Observable<User> {
return this.http
.get<unknown>(`${environment.apiUrl}/auth/me`)
.pipe(
map((raw) => this.userFromApi(raw)),
catchError((error: unknown) => toHttpError(error, {
401: 'Unauthenticated'
}))
);
}
override register(userRegistration: UserRegistration): Observable<void> {
return this.http.post<void>(`${environment.apiUrl}/auth/register`, userRegistration)
.pipe(
catchError((error: unknown) => toHttpError(error, {
400: 'Unable to register user.'
}))
);
}
override changePassword(request: ChangePasswordRequest): Observable<void> {
return this.http
.post(`${environment.apiUrl}/auth/change-password`, request, { responseType: 'text' })
.pipe(
map(() => undefined),
catchError((error: unknown) => {
if (error instanceof HttpErrorResponse) {
const message = extractHttpErrorMessage(error)
?? (error.status === 400 ? 'Missing required fields.' : null)
?? (error.status === 401 ? 'Current password is invalid' : null)
?? (error.status === 404 ? 'User not found' : null)
?? 'Unable to change password.';
return throwError(() => new Error(message));
}
return throwError(() => error);
})
);
}
private userFromApi(raw: unknown, fallbackUsername?: string): User {
const payload = (raw ?? {}) as Record<string, unknown>;
const userSource =
(payload["user"] as Record<string, unknown> | undefined)
?? (payload["profile"] as Record<string, unknown> | undefined)
?? payload;
return {
username: String(userSource["username"] ?? fallbackUsername ?? ''),
email: typeof userSource["email"] === "string" ? String(userSource["email"]) : null,
role: this.normalizeRole(userSource["role"])
} satisfies User;
}
private normalizeRole(value: unknown): UserRole {
return String(value ?? '').toUpperCase() === 'ADMIN' ? 'ADMIN' : 'USER';
}
override logout(): Observable<void> {
return this.http
.post<void>(`${environment.apiUrl}/auth/logout`, {})
.pipe(
map(() => undefined),
catchError(() => of(undefined))
);
}
}