You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Mobile/api/service/api/service.api.abstract.tsx

112 lines
2.9 KiB

import AsyncStorage from "@react-native-async-storage/async-storage";
import { router } from "expo-router";
import { getItemAsync } from "expo-secure-store";
import apiClient from "./service.api.client";
import {
ACCESS_TOKEN_PATH,
API_URL,
CLIENT_ID,
CLIENT_SECRET,
ENDPOINTS,
REFRESH_TOKEN_PATH,
} from "./service.api.constant";
type RequestOption = {
method?: string;
body?: any;
headers?: Record<string, string>;
};
export abstract class AbstractServiceApi {
protected async request(path: string, options?: RequestOption): Promise<any> {
const token = await getItemAsync(ACCESS_TOKEN_PATH);
const headers = this.buildHeaders(token, options?.headers);
const fetchOptions: RequestInit = {
method: options?.method ?? "GET",
headers,
};
if (options?.body && fetchOptions.method !== "GET") {
fetchOptions.body = JSON.stringify(options.body);
}
const res = await fetch(`${API_URL}${path}`, fetchOptions);
return await this.checkStatus(path, res, options);
}
private buildHeaders(
token?: string | null,
headers?: Record<string, string>
): Record<string, string> {
return {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
...(headers || {}),
};
}
private async checkStatus(
path: string,
res: Response,
options?: RequestOption
): Promise<any> {
switch (res.status) {
case 401:
await this.tryRefreshToken();
return this.request(path, options);
case 403:
router.replace("/(error)/NotAllowedProblem");
return;
case 500:
router.replace("/(error)/InternalErrorProblem");
return;
case 503:
router.replace("/(error)/BlockedProblem");
return;
default:
return await res.json();
}
}
private async tryRefreshToken(): Promise<boolean> {
try {
const refreshToken = await AsyncStorage.getItem(REFRESH_TOKEN_PATH);
if (!refreshToken) return false;
const response = await this.getRefreshTokenResponse(refreshToken);
if (!response.ok) return false;
const { accessToken, refreshToken: newRefreshToken } =
await response.json();
await AsyncStorage.setItem(ACCESS_TOKEN_PATH, accessToken);
await AsyncStorage.setItem(REFRESH_TOKEN_PATH, newRefreshToken);
apiClient.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;
return true;
} catch (e) {
return false;
}
}
private async getRefreshTokenResponse(
refreshToken: string
): Promise<Response> {
return await fetch(`${API_URL}/${ENDPOINTS.REFRESH_TOKEN}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
},
body: JSON.stringify({ refresh_token: refreshToken }),
});
}
}