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.
112 lines
2.9 KiB
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 }),
|
|
});
|
|
}
|
|
}
|