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; }; export abstract class AbstractServiceApi { protected async request(path: string, options?: RequestOption): Promise { 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 ): Record { return { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...(headers || {}), }; } private async checkStatus( path: string, res: Response, options?: RequestOption ): Promise { 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 { 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 { 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 }), }); } }