import Settings from 'settings'; import logger from '../utils/log'; import { headers, cookies } from 'next/headers'; import { ServerVariables } from './server-variables'; import { notFound } from 'next/navigation'; import { fixtureManager, MockMode } from '../lib/fixture-manager'; export enum FetchResponseType { JSON = 'json', TEXT = 'text' } const appFetch = async ({ url, locale, currency, init = {}, responseType = FetchResponseType.JSON }: { url: RequestInfo; locale: string; currency: string; init?: RequestInit; responseType?: FetchResponseType; }): Promise => { let response: T; let status: number; let ip = ''; try { const nextHeaders = await headers(); const nextCookies = await cookies(); ip = nextHeaders.get('x-forwarded-for') ?? ''; const commerceUrl = Settings.commerceUrl; const currentLocale = Settings.localization.locales.find( (l) => l.value === locale ); if (commerceUrl === 'default') { logger.error('Commerce URL is not set. Current value is "default"'); return undefined; } const requestURL = `${decodeURIComponent(commerceUrl)}${url}`; const mockMode = process.env.PZ_MOCK; const method = init.method?.toUpperCase() ?? 'GET'; // Replay mode: serve from fixtures without hitting the API if (mockMode === MockMode.REPLAY) { const { found, fixture } = await fixtureManager.read(method, String(url), init.body); if (found) { status = fixture.response.status; response = (responseType === FetchResponseType.JSON ? fixture.response.body : JSON.stringify(fixture.response.body)) as T; return response; } return undefined; } init.headers = { cookie: nextCookies.toString(), ...(init.headers ?? {}), ...(ServerVariables.globalHeaders ?? {}), 'Accept-Language': currentLocale.apiValue, 'x-currency': currency, 'x-forwarded-for': ip }; init.next = { revalidate: Settings.usePrettyUrlRoute ? 0 : 60 }; logger.debug(`FETCH START ${url}`, { requestURL, init, ip }); const req = await fetch(requestURL, init); status = req.status; logger.debug(`FETCH END ${url}`, { status: req.status, ip }); if (responseType === FetchResponseType.JSON) { response = (await req.json()) as T; } else { response = (await req.text()) as unknown as T; } // Record mode: save response to fixtures if (mockMode === MockMode.RECORD) { await fixtureManager.write(method, String(url), init.body, { status: req.status, headers: fixtureManager.extractHeaders(req.headers), body: response }); } logger.trace(`FETCH RESPONSE`, { url, response, ip }); } catch (error) { const logType = status === 500 ? 'fatal' : 'error'; if (!url.toString().includes('/cms/seo/')) { logger[logType](`FETCH FAILED`, { url, status, error, ip }); } } if (status === 422) { notFound(); } return response; }; export default appFetch;