"use server"; import { DEFAULT_RESPONSE, defaultHeaders, DEFAULT_TIMEOUT, nextCrypto, } from "./base"; import { FetchOptions, HttpMethod, ApiResponse } from "./types"; import { retrieveAccessToken } from "./mutual/cookies/token"; import { cookies } from "next/headers"; import { cookieObject } from "@/fetchers/base"; /** * Retrieves user selection from cookies with graceful fallback * @returns User selection object or default selection if not found */ const functionRetrieveUserSelection = async () => { try { const cookieStore = await cookies(); const encrpytUserSelection = cookieStore.get("eys-sel")?.value || ""; if (!encrpytUserSelection) { return { redisKey: "default", uuid: "", timestamp: new Date().toISOString(), }; } try { const decrpytUserSelection = await nextCrypto.decrypt( encrpytUserSelection ); if (!decrpytUserSelection) { return { redisKey: "default", uuid: "", timestamp: new Date().toISOString(), }; } return JSON.parse(decrpytUserSelection); } catch (decryptError) { return { redisKey: "default", uuid: "", timestamp: new Date().toISOString(), }; } } catch (error) { return { redisKey: "default", uuid: "", timestamp: new Date().toISOString(), }; } }; const functionSetUserSelection = async (userSelection: any) => { const cookieStore = await cookies(); const encrpytUserSelection = await nextCrypto.encrypt( JSON.stringify(userSelection) ); if (!encrpytUserSelection) throw new Error("No user selection found"); cookieStore.set({ name: "eys-sel", value: encrpytUserSelection, ...cookieObject, } as any); }; const functionRemoveUserSelection = async () => { const cookieStore = await cookies(); cookieStore.delete("eys-sel"); }; /** * 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 { const controller = new AbortController(); const fetchOptions: RequestInit = { method, headers, cache: cache ? "force-cache" : "no-cache", signal: controller.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, ]); const responseData = await response.json(); 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, functionRetrieveUserSelection, functionSetUserSelection, functionRemoveUserSelection, };