From bc026a5afe2adf95088e5c0295455b18b64c8879 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Wed, 6 May 2026 17:22:46 +0330 Subject: [PATCH] fix url --- src/app/api/proxy/route.ts | 303 ------------------ .../questions/question-answer-storage.tsx | 6 +- src/lib/http.ts | 41 +-- 3 files changed, 3 insertions(+), 347 deletions(-) delete mode 100644 src/app/api/proxy/route.ts diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts deleted file mode 100644 index bd0ceaa..0000000 --- a/src/app/api/proxy/route.ts +++ /dev/null @@ -1,303 +0,0 @@ -import type { NextRequest } from "next/server"; - -export const dynamic = "force-dynamic"; -export const runtime = "nodejs"; - -const PROXY_PATH_PARAM = "__proxyPath"; - -const REQUEST_HEADERS_TO_FORWARD = [ - "accept", - "accept-language", - "authorization", - "content-type", - "x-csrf-token", - "x-csrftoken", - "x-requested-with", - "x-xsrf-token", -]; - -const RESPONSE_HEADERS_TO_DROP = [ - "connection", - "content-encoding", - "content-length", - "keep-alive", - "proxy-authenticate", - "proxy-authorization", - "te", - "trailer", - "transfer-encoding", - "upgrade", -]; - -const MAX_LOG_BODY_LENGTH = 10_000; -const shouldLogProxy = - process.env.LOG_API_PROXY === "true" || - (process.env.LOG_API_PROXY !== "false" && - process.env.NODE_ENV !== "production"); - -class ProxyError extends Error { - constructor( - message: string, - readonly status: number, - ) { - super(message); - } -} - -function getApiBaseUrl() { - const apiBaseUrl = - process.env.API_BASE_URL ?? process.env.NEXT_PUBLIC_API_BASE_URL; - - if (!apiBaseUrl) { - throw new ProxyError( - "API_BASE_URL or NEXT_PUBLIC_API_BASE_URL is required", - 500, - ); - } - - try { - return new URL(apiBaseUrl); - } catch { - throw new ProxyError("API base URL is invalid", 500); - } -} - -function getProxyPath(request: NextRequest) { - const proxyPath = request.nextUrl.searchParams.get(PROXY_PATH_PARAM); - - if (!proxyPath) { - throw new ProxyError("Proxy path is required", 400); - } - - if (/^[a-z][a-z\d+\-.]*:/i.test(proxyPath) || proxyPath.startsWith("//")) { - throw new ProxyError("Proxy path must be relative", 400); - } - - return proxyPath.startsWith("/") ? proxyPath : `/${proxyPath}`; -} - -function getTargetUrl(request: NextRequest) { - const targetUrl = getApiBaseUrl(); - const proxyUrl = new URL(getProxyPath(request), targetUrl.origin); - const basePath = targetUrl.pathname.replace(/\/$/, ""); - - targetUrl.pathname = `${basePath}${proxyUrl.pathname}`; - - const searchParams = new URLSearchParams(proxyUrl.search); - request.nextUrl.searchParams.forEach((value, key) => { - if (key !== PROXY_PATH_PARAM) { - searchParams.append(key, value); - } - }); - targetUrl.search = searchParams.toString(); - - return targetUrl; -} - -function getRequestHeaders(request: NextRequest, targetUrl: URL) { - const headers = new Headers(); - const authKey = process.env.NEXT_PUBLIC_AUTH_KEY; - - for (const header of REQUEST_HEADERS_TO_FORWARD) { - const value = request.headers.get(header); - - if (value) { - headers.set(header, value); - } - } - - if (authKey) { - headers.set("authorization", `token ${authKey}`); - } - - headers.set("accept-encoding", "identity"); - headers.set("http_x_user_language", "en"); - headers.set("origin", targetUrl.origin); - headers.set("platform", "android"); - headers.set("referer", `${targetUrl.origin}/`); - headers.set("user-agent", "dart:io"); - - return headers; -} - -function getResponseHeaders(upstreamHeaders: Headers) { - const headers = new Headers(upstreamHeaders); - - for (const header of RESPONSE_HEADERS_TO_DROP) { - headers.delete(header); - } - - const setCookieHeaders = - ( - upstreamHeaders as Headers & { getSetCookie?: () => string[] } - ).getSetCookie?.() ?? []; - - if (setCookieHeaders.length > 0) { - headers.delete("set-cookie"); - - for (const cookie of setCookieHeaders) { - headers.append("set-cookie", cookie); - } - } - - return headers; -} - -function getBodyLogValue(body: ArrayBuffer | undefined, contentType?: string) { - if (!body || body.byteLength === 0) { - return null; - } - - if (contentType && !isTextContentType(contentType)) { - return `[${body.byteLength} bytes; ${contentType}]`; - } - - const text = new TextDecoder().decode(body); - - if (text.length <= MAX_LOG_BODY_LENGTH) { - return parseJsonForLog(text, text); - } - - return `${text.slice(0, MAX_LOG_BODY_LENGTH)}... [truncated ${text.length - MAX_LOG_BODY_LENGTH} chars]`; -} - -function isTextContentType(contentType: string) { - return ( - contentType.includes("application/json") || - contentType.includes("application/problem+json") || - contentType.startsWith("text/") || - contentType.includes("+json") || - contentType.includes("+xml") - ); -} - -function parseJsonForLog(text: string, fallback: string) { - try { - return JSON.parse(text); - } catch { - return fallback; - } -} - -function headersToObject(headers: Headers) { - return Object.fromEntries(headers.entries()); -} - -function logProxyRequest( - request: NextRequest, - targetUrl: URL, - requestHeaders: Headers, - requestBody: ArrayBuffer | undefined, -) { - if (!shouldLogProxy) { - return; - } - - writeProxyLog("request", { - incomingRequest: { - method: request.method, - url: request.url, - nextUrl: request.nextUrl.toString(), - headers: headersToObject(request.headers), - body: getBodyLogValue( - requestBody, - request.headers.get("content-type") ?? undefined, - ), - }, - upstreamRequest: { - method: request.method, - url: targetUrl.toString(), - headers: headersToObject(requestHeaders), - body: getBodyLogValue( - requestBody, - requestHeaders.get("content-type") ?? undefined, - ), - }, - payload: getBodyLogValue( - requestBody, - request.headers.get("content-type") ?? undefined, - ), - }); -} - -function logProxyResponse( - upstreamResponse: Response, - responseBody: ArrayBuffer, -) { - if (!shouldLogProxy) { - return; - } - - writeProxyLog("response", { - status: upstreamResponse.status, - statusText: upstreamResponse.statusText, - headers: headersToObject(upstreamResponse.headers), - body: getBodyLogValue( - responseBody, - upstreamResponse.headers.get("content-type") ?? undefined, - ), - response: { - status: upstreamResponse.status, - statusText: upstreamResponse.statusText, - headers: headersToObject(upstreamResponse.headers), - body: getBodyLogValue( - responseBody, - upstreamResponse.headers.get("content-type") ?? undefined, - ), - }, - }); -} - -function writeProxyLog(label: string, value: unknown) { - console.log(`[api-proxy] ${label} ${JSON.stringify(value)}`); -} - -async function proxyRequest(request: NextRequest) { - try { - const targetUrl = getTargetUrl(request); - const requestBody = - request.method === "GET" || request.method === "HEAD" - ? undefined - : await request.arrayBuffer(); - const requestHeaders = getRequestHeaders(request, targetUrl); - - logProxyRequest(request, targetUrl, requestHeaders, requestBody); - - const upstreamResponse = await fetch(targetUrl, { - method: request.method, - headers: requestHeaders, - body: requestBody, - cache: "no-store", - }); - const responseBody = await upstreamResponse.arrayBuffer(); - - logProxyResponse(upstreamResponse, responseBody); - - return new Response(responseBody, { - status: upstreamResponse.status, - statusText: upstreamResponse.statusText, - headers: getResponseHeaders(upstreamResponse.headers), - }); - } catch (error) { - if (error instanceof ProxyError) { - return Response.json({ error: error.message }, { status: error.status }); - } - - console.error("API proxy request failed", error); - - return Response.json( - { error: "API proxy request failed" }, - { status: 502 }, - ); - } -} - -export { - proxyRequest as DELETE, - proxyRequest as GET, - proxyRequest as HEAD, - proxyRequest as OPTIONS, - proxyRequest as PATCH, - proxyRequest as POST, - proxyRequest as PUT, -}; diff --git a/src/components/questions/question-answer-storage.tsx b/src/components/questions/question-answer-storage.tsx index 55c3197..bc2fb3c 100644 --- a/src/components/questions/question-answer-storage.tsx +++ b/src/components/questions/question-answer-storage.tsx @@ -243,11 +243,7 @@ function writeStoredAnswers( } function getKeepalivePatchUrl(slug: string) { - const searchParams = new URLSearchParams({ - [PROXY_PATH_PARAM]: `/api/marriage/sections/${pathParam(slug)}/data/`, - }); - - return `/api/proxy?${searchParams.toString()}`; + return `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/marriage/sections/${pathParam(slug)}/data/`; } export function QuestionAnswersProvider({ diff --git a/src/lib/http.ts b/src/lib/http.ts index 7a3f8d1..108ef41 100644 --- a/src/lib/http.ts +++ b/src/lib/http.ts @@ -1,47 +1,10 @@ -import axios, { type InternalAxiosRequestConfig } from "axios"; - -const PROXY_PATH_PARAM = "__proxyPath"; +import axios from "axios"; export const http = axios.create({ - baseURL: "/api/proxy", + baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, headers: { Accept: "application/json", "Content-Type": "application/json", }, withCredentials: true, }); - -function isAbsoluteUrl(url: string) { - return /^[a-z][a-z\d+\-.]*:\/\//i.test(url); -} - -function withProxyPathParam( - params: InternalAxiosRequestConfig["params"], - proxyPath: string, -) { - if (params instanceof URLSearchParams) { - const nextParams = new URLSearchParams(params); - nextParams.set(PROXY_PATH_PARAM, proxyPath); - return nextParams; - } - - if (typeof params === "string") { - const nextParams = new URLSearchParams(params); - nextParams.set(PROXY_PATH_PARAM, proxyPath); - return nextParams; - } - - return { - ...(params && typeof params === "object" ? params : {}), - [PROXY_PATH_PARAM]: proxyPath, - }; -} - -http.interceptors.request.use((config) => { - if (config.url && !isAbsoluteUrl(config.url)) { - config.params = withProxyPathParam(config.params, config.url); - config.url = ""; - } - - return config; -});