import {toIsArray, type TypeGuard} from "@/types"; import type {Headers} from "request"; import {parseInteger} from "@/utils/Numbers"; interface PagedListResult<T> { entries: T[]; total: number; } interface Response<T> { result: T; headers: Headers; } class Api { private baseURL: string = import.meta.env.BASE_URL; private apiPrefix = "api/"; constructor(apiPrefix?: string) { if (apiPrefix) { this.apiPrefix = apiPrefix; } } private toURL(path: string, queryParams?: object): string { let queryString = ""; if (queryParams) { const queryStrings: string[] = []; for (const [key, value] of Object.entries(queryParams)) { queryStrings.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); } if (queryStrings.length > 0) { queryString = `?${queryStrings.join("&")}`; } } return this.baseURL + this.apiPrefix + path + queryString; } private async doGet<T>(path: string, isT: TypeGuard<T>, queryParams?: object): Promise<Response<T>> { const url = this.toURL(path, queryParams); const result = await fetch(url); const json = await result.json(); if (!isT(json)) { console.log(json); throw new Error(`API get result has wrong type. ${url} => ${json}`); } return { result: json, headers: result.headers, }; } async get<T>(path: string, isT: TypeGuard<T>): Promise<T> { const response = await this.doGet(path, isT); return response.result; } async getPagedList<T>( path: string, isT: TypeGuard<T>, page: number, itemsPerPage: number, filter?: object, ): Promise<PagedListResult<T>> { const response = await this.doGet(path, toIsArray(isT), { _page: page, _perPage: itemsPerPage, ...filter, }); const totalStr = response.headers.get("x-total-count"); const total = parseInteger(totalStr, 10); return { entries: response.result, total, } } } export const api = new Api(); class InternalApi extends Api { constructor() { super("internal/api/"); } } export const internalApi = new InternalApi();