feat: hacking FAL proxy to support 3rd party API

This commit is contained in:
Jerry Tian 2025-04-09 23:57:00 -04:00
parent 6be946325b
commit 2df377550e
2 changed files with 70 additions and 8 deletions

View File

@ -0,0 +1,33 @@
#!/bin/sh
# curl -v --location --request POST 'https://api.tu-zi.com/v1/chat/completions' \
# --header 'Content-Type: application/json' \
# --header 'Authorization: Bearer sk-JTM4bQ7HeTCAALDI0c0vYTexYcDA3WtIzGUs9znsl7qOjAt2' \
# --data-raw '{
# "model": "gpt-4-gizmo-g-2fkFE8rbu",
# "messages": [
# {
# "role": "user",
# "content": "你是谁"
# }
# ],
# "stream": false
# }'
# echo
curl -v --location --request POST 'https://animator-gg-api.ca2324.servep2p.com:8443/api/fal/proxy' \
--header 'Content-Type: application/json' \
--header 'x-fal-target-url: https://api.tu-zi.com/v1/chat/completions' \
--data-raw '{
"model": "gpt-4-gizmo-g-2fkFE8rbu",
"messages": [
{
"role": "user",
"content": "你是谁"
}
],
"stream": false
}'
echo

View File

@ -10,6 +10,9 @@ export type HeaderValue = string | string[] | undefined | null;
const FAL_URL_REG_EXP = /(\.|^)fal\.(run|ai)$/;
const TUZI_API_KEY = process.env.TUZI_API_KEY;
const TUZI_URL_REG_EXP = /(\.|^)tu-zi\.com$/;
/**
* The proxy behavior that is passed to the proxy handler. This is a subset of
* request objects that are used by different frameworks, like Express and NextJS.
@ -43,7 +46,6 @@ function singleHeaderValue(value: HeaderValue): string | undefined {
}
return value;
}
function getFalKey(): string | undefined {
if (FAL_KEY) {
return FAL_KEY;
@ -54,6 +56,18 @@ function getFalKey(): string | undefined {
return undefined;
}
function getFalKeyForTargetHost(targetHost: string): string | undefined {
if (FAL_URL_REG_EXP.test(targetHost)) {
return getFalKey();
}
if (TUZI_URL_REG_EXP.test(targetHost)) {
return TUZI_API_KEY;
}
return undefined;
}
const EXCLUDED_HEADERS = ["content-length", "content-encoding"];
/**
@ -74,14 +88,29 @@ export async function handleRequest<ResponseType>(
}
const urlHost = new URL(targetUrl).host;
if (!FAL_URL_REG_EXP.test(urlHost)) {
return behavior.respondWith(412, `Invalid ${TARGET_URL_HEADER} header`);
var targetApiKey = undefined;
var targetApiAuthHeader = undefined;
if (FAL_URL_REG_EXP.test(urlHost)) {
targetApiKey = getFalKey();
targetApiAuthHeader = `Key ${targetApiKey}`;
} else if (TUZI_URL_REG_EXP.test(urlHost)) {
targetApiKey = TUZI_API_KEY;
targetApiAuthHeader = `Bearer ${targetApiKey}`;
} else {
return behavior.respondWith(
412,
`Invalid ${TARGET_URL_HEADER} header with host: ${urlHost}`,
);
}
const falKey = behavior.resolveApiKey
? await behavior.resolveApiKey()
: getFalKey();
if (!falKey) {
// const falKey = behavior.resolveApiKey
// ? await behavior.resolveApiKey()
// : getFalKeyForTargetHost(urlHost);
// if (!falKey) {
// return behavior.respondWith(401, "Missing fal.ai credentials");
// }
if (!targetApiKey) {
return behavior.respondWith(401, "Missing fal.ai credentials");
}
@ -101,7 +130,7 @@ export async function handleRequest<ResponseType>(
...headers,
authorization:
singleHeaderValue(behavior.getHeader("authorization")) ??
`Key ${falKey}`,
targetApiAuthHeader,
accept: "application/json",
"content-type": "application/json",
"user-agent": userAgent,