proxy.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import { NextRequest, NextResponse } from "next/server";
  2. export async function handle(
  3. req: NextRequest,
  4. { params }: { params: { path: string[] } },
  5. ) {
  6. console.log("[Proxy Route] params ", params);
  7. if (req.method === "OPTIONS") {
  8. return NextResponse.json({ body: "OK" }, { status: 200 });
  9. }
  10. // remove path params from searchParams
  11. req.nextUrl.searchParams.delete("path");
  12. req.nextUrl.searchParams.delete("provider");
  13. const subpath = params.path.join("/");
  14. const fetchUrl = `${req.headers.get(
  15. "x-base-url",
  16. )}/${subpath}?${req.nextUrl.searchParams.toString()}`;
  17. const skipHeaders = ["connection", "host", "origin", "referer", "cookie"];
  18. const headers = new Headers(
  19. Array.from(req.headers.entries()).filter((item) => {
  20. if (
  21. item[0].indexOf("x-") > -1 ||
  22. item[0].indexOf("sec-") > -1 ||
  23. skipHeaders.includes(item[0])
  24. ) {
  25. return false;
  26. }
  27. return true;
  28. }),
  29. );
  30. const controller = new AbortController();
  31. const fetchOptions: RequestInit = {
  32. headers,
  33. method: req.method,
  34. body: req.body,
  35. // to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
  36. redirect: "manual",
  37. // @ts-ignore
  38. duplex: "half",
  39. signal: controller.signal,
  40. };
  41. const timeoutId = setTimeout(
  42. () => {
  43. controller.abort();
  44. },
  45. 10 * 60 * 1000,
  46. );
  47. try {
  48. const res = await fetch(fetchUrl, fetchOptions);
  49. // to prevent browser prompt for credentials
  50. const newHeaders = new Headers(res.headers);
  51. newHeaders.delete("www-authenticate");
  52. // to disable nginx buffering
  53. newHeaders.set("X-Accel-Buffering", "no");
  54. // The latest version of the OpenAI API forced the content-encoding to be "br" in json response
  55. // So if the streaming is disabled, we need to remove the content-encoding header
  56. // Because Vercel uses gzip to compress the response, if we don't remove the content-encoding header
  57. // The browser will try to decode the response with brotli and fail
  58. newHeaders.delete("content-encoding");
  59. return new Response(res.body, {
  60. status: res.status,
  61. statusText: res.statusText,
  62. headers: newHeaders,
  63. });
  64. } finally {
  65. clearTimeout(timeoutId);
  66. }
  67. }