File size: 3,238 Bytes
5301c48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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<string, string>;
}

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',
    },
  });
}