proxy.ts 2.5 KB

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