diff --git a/web/next.config.js b/web/next.config.js index 1586af8d1..92812c513 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -8,47 +8,6 @@ const version = env_version || package_version; const nextConfig = { output: "standalone", swcMinify: true, - rewrites: async () => { - // In production, something else (nginx in the one box setup) should take - // care of this rewrite. TODO (chris): better support setups where - // web_server and api_server are on different machines. - if (process.env.NODE_ENV === "production") return []; - - return [ - { - source: "/api/:path*", - destination: "http://127.0.0.1:8080/:path*", // Proxy to Backend - }, - ]; - }, - redirects: async () => { - // In production, something else (nginx in the one box setup) should take - // care of this redirect. TODO (chris): better support setups where - // web_server and api_server are on different machines. - const defaultRedirects = []; - - if (process.env.NODE_ENV === "production") return defaultRedirects; - - return defaultRedirects.concat([ - { - source: "/api/chat/send-message:params*", - destination: "http://127.0.0.1:8080/chat/send-message:params*", // Proxy to Backend - permanent: true, - }, - { - source: "/api/query/stream-answer-with-quote:params*", - destination: - "http://127.0.0.1:8080/query/stream-answer-with-quote:params*", // Proxy to Backend - permanent: true, - }, - { - source: "/api/query/stream-query-validation:params*", - destination: - "http://127.0.0.1:8080/query/stream-query-validation:params*", // Proxy to Backend - permanent: true, - }, - ]); - }, publicRuntimeConfig: { version, }, diff --git a/web/src/app/api/[...path]/route.ts b/web/src/app/api/[...path]/route.ts new file mode 100644 index 000000000..550ebaf6d --- /dev/null +++ b/web/src/app/api/[...path]/route.ts @@ -0,0 +1,116 @@ +import { INTERNAL_URL } from "@/lib/constants"; +import { NextRequest, NextResponse } from "next/server"; + +/* NextJS is annoying and makes use use a separate function for +each request type >:( */ + +export async function GET( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function POST( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function PUT( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function PATCH( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function DELETE( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function HEAD( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +export async function OPTIONS( + request: NextRequest, + { params }: { params: { path: string[] } } +) { + return handleRequest(request, params.path); +} + +async function handleRequest(request: NextRequest, path: string[]) { + if (process.env.NODE_ENV !== "development") { + return NextResponse.json( + { + message: + "This API is only available in development mode. In production, something else (e.g. nginx) should handle this.", + }, + { status: 404 } + ); + } + + try { + const backendUrl = new URL(`${INTERNAL_URL}/${path.join("/")}`); + + // Get the URL parameters from the request + const urlParams = new URLSearchParams(request.url.split("?")[1]); + + // Append the URL parameters to the backend URL + urlParams.forEach((value, key) => { + backendUrl.searchParams.append(key, value); + }); + + const response = await fetch(backendUrl, { + method: request.method, + headers: request.headers, + body: request.body, + // @ts-ignore + duplex: "half", + }); + + // Check if the response is a stream + if ( + response.headers.get("Transfer-Encoding") === "chunked" || + response.headers.get("Content-Type")?.includes("stream") + ) { + // If it's a stream, create a TransformStream to pass the data through + const { readable, writable } = new TransformStream(); + response.body?.pipeTo(writable); + + return new NextResponse(readable, { + status: response.status, + headers: response.headers, + }); + } else { + return new NextResponse(response.body, { + status: response.status, + headers: response.headers, + }); + } + } catch (error: unknown) { + console.error("Proxy error:", error); + return NextResponse.json( + { + message: "Proxy error", + error: + error instanceof Error ? error.message : "An unknown error occurred", + }, + { status: 500 } + ); + } +}