import rawAxios, { AxiosError, AxiosResponse } from "axios"; import { BehaviorSubject } from "rxjs"; export const apiBaseUrl = "/api"; export const axios = rawAxios.create(); export const tokenSubject: BehaviorSubject = new BehaviorSubject< string | null >(null); tokenSubject.subscribe((token) => { if (token == null) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access delete axios.defaults.headers.common["Authorization"]; } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; } }); export function base64(blob: Blob): Promise { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = function () { resolve((reader.result as string).replace(/^data:.+;base64,/, "")); }; reader.readAsDataURL(blob); }); } export function extractStatusCode(error: AxiosError): number | null { if (error.isAxiosError) { const code = error?.response?.status; if (typeof code === "number") { return code; } } return null; } export interface CommonErrorResponse { code: number; message: string; } export function extractErrorCode( error: AxiosError ): number | null { if (error.isAxiosError) { const code = error.response?.data?.code; if (typeof code === "number") { return code; } } return null; } export class HttpNetworkError extends Error { constructor(public innerError?: AxiosError) { super(); } } export class HttpForbiddenError extends Error { constructor(public innerError?: AxiosError) { super(); } } export class NotModified {} export interface BlobWithEtag { data: Blob; etag: string; } export function extractResponseData(res: AxiosResponse): T { return res.data; } export function catchIfStatusCodeIs< TResult, TErrorHandlerResult extends TResult | PromiseLike | null | undefined >( statusCode: number, errorHandler: (error: AxiosError) => TErrorHandlerResult ): (error: AxiosError) => TErrorHandlerResult { return (error: AxiosError) => { if (extractStatusCode(error) == statusCode) { return errorHandler(error); } else { throw error; } }; } export function convertToIfStatusCodeIs( statusCode: number, newErrorType: { new (innerError: AxiosError): NewError; } ): (error: AxiosError) => never { return catchIfStatusCodeIs(statusCode, (error) => { throw new newErrorType(error); }); } export function catchIfErrorCodeIs< TResult, TErrorHandlerResult extends TResult | PromiseLike | null | undefined >( errorCode: number, errorHandler: (error: AxiosError) => TErrorHandlerResult ): (error: AxiosError) => TErrorHandlerResult { return (error: AxiosError) => { if (extractErrorCode(error) == errorCode) { return errorHandler(error); } else { throw error; } }; } export function convertToIfErrorCodeIs( errorCode: number, newErrorType: { new (innerError: AxiosError): NewError; } ): (error: AxiosError) => never { return catchIfErrorCodeIs(errorCode, (error) => { throw new newErrorType(error); }); } export function convertToNetworkError( error: AxiosError ): never { if (error.isAxiosError && error.response == null) { throw new HttpNetworkError(error); } else { throw error; } } export function convertToForbiddenError( error: AxiosError ): never { if ( error.isAxiosError && error.response != null && (error.response.status == 401 || error.response.status == 403) ) { throw new HttpForbiddenError(error); } else { throw error; } } export function convertToNotModified( error: AxiosError ): NotModified { if ( error.isAxiosError && error.response != null && error.response.status == 304 ) { return new NotModified(); } else { throw error; } } export function convertToBlobWithEtag(res: AxiosResponse): BlobWithEtag { return { data: res.data, etag: (res.headers as Record<"etag", string>)["etag"], }; }