import { getNewTokensWithRefreshToken } from "@/features/auth/services/auth.service"; import { ApiResponse } from "@/types/api.types"; import axios from "axios"; import { cookies, headers } from "next/headers"; import { envVars } from "../env"; import { isTokenExpiringSoon } from "../utils/token"; if (!envVars.API_URL) { throw new Error("API_BASE_URL is not defined in environment variables"); } async function tryRefreshToken( accessToken: string, refreshToken: string, ): Promise { if (!(await isTokenExpiringSoon(accessToken))) { return; } const requestHeader = await headers(); if (requestHeader.get("x-token-refreshed") === "1") { return; } try { await getNewTokensWithRefreshToken(refreshToken); } catch (error: unknown) { console.error("Error refreshing token in http client:", error); } } const axiosInstance = async () => { const cookieStore = await cookies(); const accessToken = cookieStore.get("accessToken")?.value; const refreshToken = cookieStore.get("refreshToken")?.value; if (accessToken && refreshToken) { await tryRefreshToken(accessToken, refreshToken); } const cookieHeader = cookieStore .getAll() .map((cookie) => `${cookie.name}=${cookie.value}`) .join("; "); const instance = axios.create({ baseURL: envVars.API_URL, timeout: 30000, headers: { "Content-Type": "application/json", Cookie: cookieHeader, }, }); return instance; }; export interface ApiRequestOptions { params?: Record; headers?: Record; } const httpGet = async ( endpoint: string, options?: ApiRequestOptions, ): Promise> => { try { const instance = await axiosInstance(); const response = await instance.get>(endpoint, { params: options?.params, headers: options?.headers, }); return response.data; } catch (error) { console.error(`GET request to ${endpoint} failed:`, error); throw error; } }; const httpPost = async ( endpoint: string, data: unknown, options?: ApiRequestOptions, ): Promise> => { try { const instance = await axiosInstance(); const response = await instance.post>(endpoint, data, { params: options?.params, headers: options?.headers, }); return response.data; } catch (error) { console.error(`POST request to ${endpoint} failed:`, error); throw error; } }; const httpPut = async ( endpoint: string, data: unknown, options?: ApiRequestOptions, ): Promise> => { try { const instance = await axiosInstance(); const response = await instance.put>(endpoint, data, { params: options?.params, headers: options?.headers, }); return response.data; } catch (error) { console.error(`PUT request to ${endpoint} failed:`, error); throw error; } }; const httpPatch = async ( endpoint: string, data: unknown, options?: ApiRequestOptions, ): Promise> => { try { const instance = await axiosInstance(); const response = await instance.patch>(endpoint, data, { params: options?.params, headers: options?.headers, }); return response.data; } catch (error) { console.error(`PATCH request to ${endpoint} failed:`, error); throw error; } }; const httpDelete = async ( endpoint: string, options?: ApiRequestOptions, ): Promise> => { try { const instance = await axiosInstance(); const response = await instance.delete>(endpoint, { params: options?.params, headers: options?.headers, }); return response.data; } catch (error) { console.error(`DELETE request to ${endpoint} failed:`, error); throw error; } }; export const api = { get: httpGet, post: httpPost, put: httpPut, patch: httpPatch, delete: httpDelete, };