Files
narlblog/frontend/src/pages/api/[...path].ts
T
2026-05-17 14:44:45 +02:00

75 lines
2.4 KiB
TypeScript

import type { APIRoute } from 'astro';
const FORBIDDEN_HEADERS = new Set([
'host', 'connection', 'content-length', 'transfer-encoding', 'origin', 'referer',
'accept-encoding',
]);
// Node fetch auto-decompresses the response body, so any encoding/length
// headers from the upstream no longer match what we forward to the browser.
const FORBIDDEN_RESPONSE_HEADERS = new Set([
'content-encoding', 'content-length', 'transfer-encoding',
]);
export const ALL: APIRoute = async ({ request, params }) => {
const API_URL = process.env.PUBLIC_API_URL || 'http://backend:3000';
const path = params.path;
if (!path) {
return new Response(JSON.stringify({ error: 'Missing path' }), { status: 400 });
}
const url = new URL(`${API_URL}/api/${path}`);
const requestUrl = new URL(request.url);
url.search = requestUrl.search;
const headers = new Headers();
request.headers.forEach((value, key) => {
if (!FORBIDDEN_HEADERS.has(key.toLowerCase())) {
headers.set(key, value);
}
});
try {
const fetchOptions: RequestInit = {
method: request.method,
headers,
};
if (request.method !== 'GET' && request.method !== 'HEAD' && request.body) {
fetchOptions.body = request.body;
// @ts-expect-error — required by Node fetch when body is a stream
fetchOptions.duplex = 'half';
}
const response = await fetch(url.toString(), fetchOptions);
const responseHeaders = new Headers();
response.headers.forEach((value, key) => {
const k = key.toLowerCase();
// Set-Cookie can repeat and must NOT be merged. Handle it separately below.
if (k === 'set-cookie') return;
if (FORBIDDEN_RESPONSE_HEADERS.has(k)) return;
responseHeaders.set(key, value);
});
// getSetCookie is present on Node/undici Headers; type it locally so we
// neither depend on a specific @types/node nor need a ts-suppression.
const h = response.headers as Headers & { getSetCookie?: () => string[] };
const setCookies: string[] = h.getSetCookie?.() ?? [];
for (const c of setCookies) {
responseHeaders.append('set-cookie', c);
}
return new Response(response.body, {
status: response.status,
headers: responseHeaders,
});
} catch (e) {
console.error(`[Proxy] ${request.method} ${url} failed:`, e);
return new Response(JSON.stringify({ error: 'Proxy connection failed' }), {
status: 502,
headers: { 'Content-Type': 'application/json' },
});
}
};