@fal-ai/serverless-proxy
Version:
Deprecation note: this library has been deprecated in favor of @fal-ai/server-proxy
107 lines • 3.96 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveApiKeyFromEnv = exports.responsePassthrough = exports.DEFAULT_PROXY_ROUTE = exports.TARGET_URL_HEADER = void 0;
exports.handleRequest = handleRequest;
exports.fromHeaders = fromHeaders;
exports.TARGET_URL_HEADER = "x-fal-target-url";
exports.DEFAULT_PROXY_ROUTE = "/api/fal/proxy";
const FAL_KEY = process.env.FAL_KEY;
const FAL_KEY_ID = process.env.FAL_KEY_ID;
const FAL_KEY_SECRET = process.env.FAL_KEY_SECRET;
const FAL_URL_REG_EXP = /(\.|^)fal\.(run|ai)$/;
/**
* Utility to get a header value as `string` from a Headers object.
*
* @private
* @param request the header value.
* @returns the header value as `string` or `undefined` if the header is not set.
*/
function singleHeaderValue(value) {
if (!value) {
return undefined;
}
if (Array.isArray(value)) {
return value[0];
}
return value;
}
function getFalKey() {
if (FAL_KEY) {
return FAL_KEY;
}
if (FAL_KEY_ID && FAL_KEY_SECRET) {
return `${FAL_KEY_ID}:${FAL_KEY_SECRET}`;
}
return undefined;
}
const EXCLUDED_HEADERS = ["content-length", "content-encoding"];
/**
* A request handler that proxies the request to the fal-serverless
* endpoint. This is useful so client-side calls to the fal-serverless endpoint
* can be made without CORS issues and the correct credentials can be added
* effortlessly.
*
* @param behavior the request proxy behavior.
* @returns Promise<any> the promise that will be resolved once the request is done.
*/
async function handleRequest(behavior) {
const targetUrl = singleHeaderValue(behavior.getHeader(exports.TARGET_URL_HEADER));
if (!targetUrl) {
return behavior.respondWith(400, `Missing the ${exports.TARGET_URL_HEADER} header`);
}
const urlHost = new URL(targetUrl).host;
if (!FAL_URL_REG_EXP.test(urlHost)) {
return behavior.respondWith(412, `Invalid ${exports.TARGET_URL_HEADER} header`);
}
const falKey = behavior.resolveApiKey
? await behavior.resolveApiKey()
: getFalKey();
if (!falKey) {
return behavior.respondWith(401, "Missing fal.ai credentials");
}
// pass over headers prefixed with x-fal-*
const headers = {};
Object.keys(behavior.getHeaders()).forEach((key) => {
if (key.toLowerCase().startsWith("x-fal-")) {
headers[key.toLowerCase()] = behavior.getHeader(key);
}
});
const proxyUserAgent = `@fal-ai/serverless-proxy/${behavior.id}`;
const userAgent = singleHeaderValue(behavior.getHeader("user-agent"));
const res = await fetch(targetUrl, {
method: behavior.method,
headers: {
...headers,
authorization: singleHeaderValue(behavior.getHeader("authorization")) ??
`Key ${falKey}`,
accept: "application/json",
"content-type": "application/json",
"user-agent": userAgent,
"x-fal-client-proxy": proxyUserAgent,
},
body: behavior.method?.toUpperCase() === "GET"
? undefined
: await behavior.getRequestBody(),
});
// copy headers from fal to the proxied response
res.headers.forEach((value, key) => {
if (!EXCLUDED_HEADERS.includes(key.toLowerCase())) {
behavior.sendHeader(key, value);
}
});
return behavior.sendResponse(res);
}
function fromHeaders(headers) {
// TODO once Header.entries() is available, use that instead
// Object.fromEntries(headers.entries());
const result = {};
headers.forEach((value, key) => {
result[key] = value;
});
return result;
}
const responsePassthrough = (res) => Promise.resolve(res);
exports.responsePassthrough = responsePassthrough;
const resolveApiKeyFromEnv = () => Promise.resolve(getFalKey());
exports.resolveApiKeyFromEnv = resolveApiKeyFromEnv;
//# sourceMappingURL=index.js.map