151 lines
4.3 KiB
TypeScript
151 lines
4.3 KiB
TypeScript
|
|
import { Injectable, inject } from '@angular/core';
|
||
|
|
import { HttpClient, HttpParams, HttpHeaders, HttpEvent, HttpEventType, HttpRequest } from '@angular/common/http';
|
||
|
|
import { Observable, map, filter } from 'rxjs';
|
||
|
|
import { environment } from '../../../environments/environment';
|
||
|
|
|
||
|
|
export interface UploadProgress<T> {
|
||
|
|
progress: number;
|
||
|
|
loaded: number;
|
||
|
|
total: number;
|
||
|
|
complete: boolean;
|
||
|
|
response?: T;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface ApiResponse<T> {
|
||
|
|
success: boolean;
|
||
|
|
data: T;
|
||
|
|
timestamp: string;
|
||
|
|
message?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface PaginatedResponse<T> {
|
||
|
|
data: T[];
|
||
|
|
total: number;
|
||
|
|
page: number;
|
||
|
|
limit: number;
|
||
|
|
totalPages: number;
|
||
|
|
hasNextPage: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
@Injectable({
|
||
|
|
providedIn: 'root',
|
||
|
|
})
|
||
|
|
export class ApiService {
|
||
|
|
private readonly http = inject(HttpClient);
|
||
|
|
private readonly baseUrl = environment.apiBaseUrl;
|
||
|
|
|
||
|
|
get<T>(path: string, params?: Record<string, string | number | boolean>): Observable<T> {
|
||
|
|
let httpParams = new HttpParams();
|
||
|
|
if (params) {
|
||
|
|
Object.entries(params).forEach(([key, value]) => {
|
||
|
|
if (value !== undefined && value !== null) {
|
||
|
|
httpParams = httpParams.set(key, String(value));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return this.http
|
||
|
|
.get<ApiResponse<T>>(`${this.baseUrl}${path}`, { params: httpParams })
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
getRaw<T>(path: string, params?: Record<string, string | number | boolean>): Observable<T> {
|
||
|
|
let httpParams = new HttpParams();
|
||
|
|
if (params) {
|
||
|
|
Object.entries(params).forEach(([key, value]) => {
|
||
|
|
if (value !== undefined && value !== null) {
|
||
|
|
httpParams = httpParams.set(key, String(value));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return this.http.get<T>(`${this.baseUrl}${path}`, { params: httpParams });
|
||
|
|
}
|
||
|
|
|
||
|
|
post<T>(path: string, body: unknown): Observable<T> {
|
||
|
|
return this.http
|
||
|
|
.post<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
postRaw<T>(path: string, body: unknown): Observable<T> {
|
||
|
|
return this.http.post<T>(`${this.baseUrl}${path}`, body);
|
||
|
|
}
|
||
|
|
|
||
|
|
put<T>(path: string, body: unknown): Observable<T> {
|
||
|
|
return this.http
|
||
|
|
.put<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
patch<T>(path: string, body: unknown): Observable<T> {
|
||
|
|
return this.http
|
||
|
|
.patch<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
delete<T>(path: string): Observable<T> {
|
||
|
|
return this.http
|
||
|
|
.delete<ApiResponse<T>>(`${this.baseUrl}${path}`)
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
upload<T>(path: string, formData: FormData): Observable<T> {
|
||
|
|
return this.http
|
||
|
|
.post<ApiResponse<T>>(`${this.baseUrl}${path}`, formData)
|
||
|
|
.pipe(map((response) => response.data));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Upload with progress tracking
|
||
|
|
* Returns an observable that emits upload progress and final response
|
||
|
|
*/
|
||
|
|
uploadWithProgress<T>(path: string, formData: FormData): Observable<UploadProgress<T>> {
|
||
|
|
const req = new HttpRequest('POST', `${this.baseUrl}${path}`, formData, {
|
||
|
|
reportProgress: true,
|
||
|
|
});
|
||
|
|
|
||
|
|
return this.http.request<ApiResponse<T>>(req).pipe(
|
||
|
|
map((event: HttpEvent<ApiResponse<T>>) => {
|
||
|
|
switch (event.type) {
|
||
|
|
case HttpEventType.UploadProgress:
|
||
|
|
const total = event.total || 0;
|
||
|
|
const loaded = event.loaded;
|
||
|
|
const progress = total > 0 ? Math.round((loaded / total) * 100) : 0;
|
||
|
|
return {
|
||
|
|
progress,
|
||
|
|
loaded,
|
||
|
|
total,
|
||
|
|
complete: false,
|
||
|
|
} as UploadProgress<T>;
|
||
|
|
|
||
|
|
case HttpEventType.Response:
|
||
|
|
return {
|
||
|
|
progress: 100,
|
||
|
|
loaded: event.body?.data ? 1 : 0,
|
||
|
|
total: 1,
|
||
|
|
complete: true,
|
||
|
|
response: event.body?.data,
|
||
|
|
} as UploadProgress<T>;
|
||
|
|
|
||
|
|
default:
|
||
|
|
return {
|
||
|
|
progress: 0,
|
||
|
|
loaded: 0,
|
||
|
|
total: 0,
|
||
|
|
complete: false,
|
||
|
|
} as UploadProgress<T>;
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
download(path: string): Observable<Blob> {
|
||
|
|
return this.http.get(`${this.baseUrl}${path}`, {
|
||
|
|
responseType: 'blob',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
getBlob(url: string): Observable<Blob> {
|
||
|
|
return this.http.get(url, { responseType: 'blob' });
|
||
|
|
}
|
||
|
|
}
|