feat(api): add unified v2 gateway and mobile read slice
This commit is contained in:
75
backend/unified-api/src/routes/proxy.js
Normal file
75
backend/unified-api/src/routes/proxy.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Router } from 'express';
|
||||
import { AppError } from '../lib/errors.js';
|
||||
|
||||
const HOP_BY_HOP_HEADERS = new Set([
|
||||
'connection',
|
||||
'content-length',
|
||||
'host',
|
||||
'keep-alive',
|
||||
'proxy-authenticate',
|
||||
'proxy-authorization',
|
||||
'te',
|
||||
'trailer',
|
||||
'transfer-encoding',
|
||||
'upgrade',
|
||||
]);
|
||||
|
||||
function resolveTargetBase(pathname) {
|
||||
if (pathname.startsWith('/core')) return process.env.CORE_API_BASE_URL;
|
||||
if (pathname.startsWith('/commands')) return process.env.COMMAND_API_BASE_URL;
|
||||
if (pathname.startsWith('/query')) return process.env.QUERY_API_BASE_URL;
|
||||
return null;
|
||||
}
|
||||
|
||||
function copyHeaders(source, target) {
|
||||
for (const [key, value] of source.entries()) {
|
||||
if (HOP_BY_HOP_HEADERS.has(key.toLowerCase())) continue;
|
||||
target.setHeader(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
async function forwardRequest(req, res, next, fetchImpl) {
|
||||
try {
|
||||
const requestPath = new URL(req.originalUrl, 'http://localhost').pathname;
|
||||
const baseUrl = resolveTargetBase(requestPath);
|
||||
if (!baseUrl) {
|
||||
throw new AppError('NOT_FOUND', `No upstream configured for ${requestPath}`, 404);
|
||||
}
|
||||
|
||||
const url = new URL(req.originalUrl, baseUrl);
|
||||
const headers = new Headers();
|
||||
for (const [key, value] of Object.entries(req.headers)) {
|
||||
if (value == null || HOP_BY_HOP_HEADERS.has(key.toLowerCase())) continue;
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) headers.append(key, item);
|
||||
} else {
|
||||
headers.set(key, value);
|
||||
}
|
||||
}
|
||||
headers.set('x-request-id', req.requestId);
|
||||
|
||||
const upstream = await fetchImpl(url, {
|
||||
method: req.method,
|
||||
headers,
|
||||
body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req,
|
||||
duplex: req.method === 'GET' || req.method === 'HEAD' ? undefined : 'half',
|
||||
});
|
||||
|
||||
copyHeaders(upstream.headers, res);
|
||||
res.status(upstream.status);
|
||||
|
||||
const buffer = Buffer.from(await upstream.arrayBuffer());
|
||||
return res.send(buffer);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function createProxyRouter(options = {}) {
|
||||
const router = Router();
|
||||
const fetchImpl = options.fetchImpl || fetch;
|
||||
|
||||
router.use(['/core', '/commands', '/query'], (req, res, next) => forwardRequest(req, res, next, fetchImpl));
|
||||
|
||||
return router;
|
||||
}
|
||||
Reference in New Issue
Block a user