"use server"; import { retrieveAccessToken } from "@/apicalls/cookies/endpoints"; 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); }); }; /** * Prepares a standardized API response * @param response The response data * @param statusCode HTTP status code * @returns Standardized API response */ const prepareResponse = ( response: T, statusCode: number ): ApiResponse => { try { return { status: statusCode, data: response || ({} as T), }; } catch (error) { console.error("Error preparing response:", error); return { ...DEFAULT_RESPONSE, error: "Response parsing error", } as ApiResponse; } }; /** * 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 { const controller = new AbortController(); const signal = controller.signal; const fetchOptions: RequestInit = { method, headers, cache: cache ? "force-cache" : "no-cache", signal, }; if (method !== "GET" && payload) { fetchOptions.body = JSON.stringify( payload.payload ? payload.payload : payload ); } const timeoutPromise = createTimeoutPromise(timeout, controller); const response = (await Promise.race([ fetch(url, fetchOptions), timeoutPromise, ])) as Response; const responseJson = await response.json(); if (process.env.NODE_ENV !== "production") { console.log("Fetching:", url, fetchOptions); // console.log("Response:", responseJson); } return prepareResponse(responseJson, response.status); } catch (error) { console.error(`Fetch 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 };