This commit is contained in:
Vula Builder
2026-07-15 09:25:30 +00:00
parent a88cc5d4c3
commit 457f36b48c
+74
View File
@@ -0,0 +1,74 @@
import { NextRequest, NextResponse } from 'next/server'
import http from 'node:http'
import https from 'node:https'
// Service-type routing: NEXT_PUBLIC_MEDUSA_SERVICE_TYPE (from .env.local) selects the correct
// internal Medusa container. Falls back to ecommerce.
const _st = process.env.NEXT_PUBLIC_MEDUSA_SERVICE_TYPE || 'ecommerce'
const _urlMap: Record<string, string> = {
ecommerce: process.env.VULA_ECOMMERCE_BACKEND_URL || 'http://vula-ecommerce:9000',
hotel: process.env.VULA_HOTEL_BACKEND_URL || 'http://vula-hotel:9000',
restaurant: process.env.VULA_RESTAURANT_BACKEND_URL || 'http://vula-restaurant:9000',
services: process.env.VULA_SERVICES_BACKEND_URL || 'http://vula-services:9000',
}
const MEDUSA_URL = _urlMap[_st] || _urlMap.ecommerce
function nodeRequest(
url: string, method: string, headers: Record<string, string>, body?: string, timeoutMs = 15000
): Promise<{ status: number; data: unknown }> {
return new Promise((resolve, reject) => {
const parsed = new URL(url)
const lib = parsed.protocol === 'https:' ? https : http
const req = lib.request({
hostname: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search,
method,
headers: { ...headers, ...(body ? { 'content-length': Buffer.byteLength(body).toString() } : {}) },
}, (res) => {
let raw = ''
res.setEncoding('utf8')
res.on('data', (chunk) => { raw += chunk })
res.on('end', () => {
try { resolve({ status: res.statusCode ?? 200, data: JSON.parse(raw) }) }
catch { resolve({ status: res.statusCode ?? 200, data: {} }) }
})
})
req.setTimeout(timeoutMs, () => { req.destroy(new Error('timeout')) })
req.on('error', reject)
if (body) req.write(body)
req.end()
})
}
async function proxy(req: NextRequest, segments: string[], method: string, body?: string) {
const search = req.nextUrl.search
const url = `${MEDUSA_URL}/${segments.join('/')}${search}`
const headers: Record<string, string> = { 'content-type': 'application/json', accept: 'application/json' }
const pubKey = req.headers.get('x-publishable-api-key') || process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || ''
if (pubKey) headers['x-publishable-api-key'] = pubKey
const timeoutMs = (method === 'POST' && segments.includes('carts')) ? 25000 : 15000
try {
const { status, data } = await nodeRequest(url, method, headers, body, timeoutMs)
return NextResponse.json(data, { status })
} catch {
return NextResponse.json({ error: 'Medusa proxy error' }, { status: 502 })
}
}
type RouteCtx = { params: { path: string[] } }
export async function GET(req: NextRequest, { params }: RouteCtx) {
return proxy(req, params.path, 'GET')
}
export async function POST(req: NextRequest, { params }: RouteCtx) {
return proxy(req, params.path, 'POST', await req.text())
}
export async function PUT(req: NextRequest, { params }: RouteCtx) {
return proxy(req, params.path, 'PUT', await req.text())
}
export async function PATCH(req: NextRequest, { params }: RouteCtx) {
return proxy(req, params.path, 'PATCH', await req.text())
}
export async function DELETE(req: NextRequest, { params }: RouteCtx) {
return proxy(req, params.path, 'DELETE')
}