import { NextRequest, NextResponse } from 'next/server'; const TARGET_SERVER_BASE_URL = process.env.SERVER_BASE_URL || 'http://localhost:8002'; interface ProxyOptions { method?: string; headers?: Record; } export async function proxyToBackend( req: NextRequest, endpoint: string, options: ProxyOptions = {} ) { try { const method = options.method || req.method; const targetUrl = `${TARGET_SERVER_BASE_URL}${endpoint}`; // Prepare request options const fetchOptions: RequestInit = { method, headers: { 'Content-Type': 'application/json', ...options.headers, }, }; // Add body for POST/PUT/PATCH/DELETE requests (DELETE may have a body) if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) { try { const requestBody = await req.json(); if (requestBody && Object.keys(requestBody).length > 0) { fetchOptions.body = JSON.stringify(requestBody); } } catch (error) { // No body or invalid JSON - this is fine for DELETE requests if (method !== 'DELETE') { console.warn(`Failed to parse request body for ${method}:`, error); } } } // For GET and DELETE requests, append query parameters if they exist if (['GET', 'DELETE'].includes(method)) { const url = new URL(req.url); const searchParams = url.searchParams; if (searchParams.toString()) { const targetUrlWithParams = `${targetUrl}?${searchParams.toString()}`; const backendResponse = await fetch(targetUrlWithParams, fetchOptions); return handleBackendResponse(backendResponse, endpoint); } } // Make the actual request to the backend service const backendResponse = await fetch(targetUrl, fetchOptions); return handleBackendResponse(backendResponse, endpoint); } catch (error) { console.error(`Error in ${endpoint} route:`, error); return new NextResponse('Internal server error', { status: 500 }); } } async function handleBackendResponse(backendResponse: Response, endpoint: string) { // If the backend service returned an error, forward that error to the client if (!backendResponse.ok) { const errorBody = await backendResponse.text(); const errorHeaders = new Headers(); backendResponse.headers.forEach((value, key) => { errorHeaders.set(key, value); }); return new NextResponse(errorBody, { status: backendResponse.status, statusText: backendResponse.statusText, headers: errorHeaders, }); } // Ensure the backend response has a body to stream if (!backendResponse.body) { return new NextResponse('Stream body from backend is null', { status: 500 }); } return new NextResponse(backendResponse.body, { status: backendResponse.status, statusText: backendResponse.statusText, headers: backendResponse.headers, }); } export function createCORSResponse() { return new NextResponse(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, }); }