import type { NextRequest } from "next/server"; const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization"; const GIS_UPLOAD_PATH = "/sewerage-service/api/v1/map/gisUploads"; const HOP_BY_HOP_HEADERS = [ "connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailer", "transfer-encoding", "upgrade", ]; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; export const maxDuration = 600; /** * multipart 본문을 한 번 버퍼링해 백엔드에 정확한 Content-Length로 전달한다. * 로컬 Node Web Stream의 chunked 재전송 지연을 피하며, 허용 크기는 백엔드의 50MB 제한을 따른다. */ export async function POST(request: NextRequest) { const sewerageApiUrl = process.env.SEWERAGE_API_URL?.trim(); if (!sewerageApiUrl) { return Response.json( { status: 503, message: "GIS 업로드 서버 주소가 설정되지 않았습니다.", code: "GIS_UPLOAD_PROXY_NOT_CONFIGURED", }, { status: 503 }, ); } if (!request.body) { return Response.json( { status: 400, message: "GIS 업로드 요청 본문이 없습니다.", code: "GIS_UPLOAD_BODY_REQUIRED", }, { status: 400 }, ); } const requestHeaders = new Headers(request.headers); HOP_BY_HOP_HEADERS.forEach((headerName) => requestHeaders.delete(headerName)); requestHeaders.delete("host"); // 원 요청 길이는 Next가 다시 구성한 multipart 본문 길이와 다를 수 있어 아래에서 다시 설정한다. requestHeaders.delete("content-length"); // undici fetch는 curl 등이 대용량 요청에 붙이는 Expect: 100-continue 전달을 지원하지 않는다. requestHeaders.delete("expect"); // middleware rewrite와 동일하게 로그인 쿠키의 인증 값을 백엔드 요청 헤더에 주입한다. const token = request.cookies.get(CLAIM_NAME)?.value; const userId = request.cookies.get(AUTH_USER_ID)?.value; if (token) requestHeaders.set(CLAIM_NAME, token); if (userId) requestHeaders.set(AUTH_USER_ID, userId); const targetUrl = `${sewerageApiUrl.replace(/\/$/, "")}${GIS_UPLOAD_PATH}`; try { const requestBody = await request.arrayBuffer(); requestHeaders.set("content-length", String(requestBody.byteLength)); const upstreamResponse = await fetch(targetUrl, { method: "POST", headers: requestHeaders, body: requestBody, cache: "no-store", signal: request.signal, }); const responseHeaders = new Headers(upstreamResponse.headers); HOP_BY_HOP_HEADERS.forEach((headerName) => responseHeaders.delete(headerName), ); return new Response(upstreamResponse.body, { status: upstreamResponse.status, statusText: upstreamResponse.statusText, headers: responseHeaders, }); } catch (error) { console.error("[GIS_UPLOAD_PROXY] 백엔드 업로드 요청 실패", error); return Response.json( { status: 502, message: "GIS 업로드 서버 연결이 중단되었습니다. 잠시 후 다시 시도하세요.", code: "GIS_UPLOAD_PROXY_FAILED", }, { status: 502 }, ); } }