"use server"; import { retrieveAccessToken } from "@/apifetchers/mutual/cookies/token"; import { DEFAULT_RESPONSE, defaultHeaders, FetchOptions, HttpMethod, ApiResponse, DEFAULT_TIMEOUT, } from "./basics"; /** * Creates a promise that rejects after a specified timeout * @param ms Timeout in milliseconds * @param controller AbortController to abort the fetch request * @returns A promise that rejects after the timeout */ const createTimeoutPromise = ( ms: number, controller: AbortController ): Promise => { return new Promise((_, reject) => { setTimeout(() => { controller.abort(); reject(new Error(`Request timed out after ${ms}ms`)); }, ms); }); }; /** * Core fetch function with timeout and error handling * @param url The URL to fetch * @param options Fetch options * @param headers Request headers * @param payload Request payload * @returns API response */ async function coreFetch( url: string, options: FetchOptions = {}, headers: Record = defaultHeaders, payload?: any ): Promise> { const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options; try { // Setup controller for timeout handling const controller = new AbortController(); // Prepare fetch options const fetchOptions: RequestInit = { method, headers, cache: cache ? "force-cache" : "no-cache", signal: controller.signal, }; // Add body for non-GET requests with payload if (method !== "GET" && payload) { fetchOptions.body = JSON.stringify( payload.payload ? payload.payload : payload ); } // Create timeout promise const timeoutPromise = createTimeoutPromise(timeout, controller); // Execute request with timeout const response = await Promise.race([ fetch(url, fetchOptions), timeoutPromise, ]); // Parse response const responseData = await response.json(); // Return standardized response return { status: response.status, data: responseData || ({} as T), }; } catch (error) { console.error(`API Error (${url}):`, error); return { ...DEFAULT_RESPONSE, error: error instanceof Error ? error.message : "Network error", } as ApiResponse; } } /** * Fetch data without authentication */ async function fetchData( endpoint: string, payload?: any, method: HttpMethod = "POST", cache: boolean = false, timeout: number = DEFAULT_TIMEOUT ): Promise> { return coreFetch( endpoint, { method, cache, timeout }, defaultHeaders, payload ); } /** * Fetch data with authentication token */ async function fetchDataWithToken( endpoint: string, payload?: any, method: HttpMethod = "POST", cache: boolean = false, timeout: number = DEFAULT_TIMEOUT ): Promise> { const accessToken = (await retrieveAccessToken()) || ""; const headers = { ...defaultHeaders, "eys-acs-tkn": accessToken, }; return coreFetch(endpoint, { method, cache, timeout }, headers, payload); } /** * Update data with authentication token and UUID */ async function updateDataWithToken( endpoint: string, uuid: string, payload?: any, method: HttpMethod = "POST", cache: boolean = false, timeout: number = DEFAULT_TIMEOUT ): Promise> { const accessToken = (await retrieveAccessToken()) || ""; const headers = { ...defaultHeaders, "eys-acs-tkn": accessToken, }; return coreFetch( `${endpoint}/${uuid}`, { method, cache, timeout }, headers, payload ); } export { fetchData, fetchDataWithToken, updateDataWithToken };