zappier-edge/src/upstream.ts

47 lines
1.6 KiB
TypeScript

import { Request, Response } from 'express';
export type UpstreamFetch = typeof fetch;
/**
* After zappier meters the call, forward Verae operations to middleware.
* When ZAPPIER_UPSTREAM is unset, callers keep the local mock.
*/
export async function proxyVerae(
req: Request,
res: Response,
pathname: string,
fetchImpl: UpstreamFetch = fetch,
): Promise<boolean> {
const base = (process.env.ZAPPIER_UPSTREAM || '').replace(/\/$/, '');
if (!base) return false;
const url = new URL(pathname, `${base}/`);
for (const [k, v] of Object.entries(req.query)) {
if (typeof v === 'string') url.searchParams.set(k, v);
}
const headers: Record<string, string> = { accept: 'application/json' };
const key = req.header('x-api-key');
if (key) headers['x-api-key'] = key;
const auth = req.header('authorization');
if (auth) headers.authorization = auth;
const veraeUserId = req.customer?.veraeUserId;
if (veraeUserId) headers['x-verae-user-id'] = veraeUserId;
const method = req.method.toUpperCase();
const init: RequestInit = { method, headers };
if (method !== 'GET' && method !== 'HEAD') {
headers['content-type'] = 'application/json';
init.body = JSON.stringify(req.body ?? {});
}
const r = await fetchImpl(url.toString(), init);
const text = await r.text();
let body: unknown = text;
try {
body = text ? JSON.parse(text) : {};
} catch {
/* keep text */
}
if (body && typeof body === 'object' && !Array.isArray(body) && res.locals.quote) {
(body as Record<string, unknown>).quote = res.locals.quote;
}
res.status(r.status).json(body);
return true;
}