import { auth } from "@/app/(auth)/auth"; import { getSystemSetting } from "@/lib/db/queries"; const ALLOWED_FAL_HOSTS = new Set(["rest.alpha.fal.ai", "rest.fal.ai"]); type FalRealtimeConfig = { enabled?: boolean; falApiKey?: string; apiKey?: string; }; type AngleEditConfig = { falApiKey?: string; }; async function getRealtimeCanvasConfig() { const [realtimeSetting, angleEditSetting] = await Promise.all([ getSystemSetting({ key: "realtime_canvas_config" }).catch(() => null), getSystemSetting({ key: "angle_edit_config" }).catch(() => null), ]); const realtimeConfig = (realtimeSetting?.value || {}) as FalRealtimeConfig; const angleEditConfig = (angleEditSetting?.value || {}) as AngleEditConfig; const fallbackKey = String( angleEditConfig.falApiKey || process.env.FAL_KEY || process.env.FAL_API_KEY || "" ).trim(); return { ...realtimeConfig, falApiKey: String(realtimeConfig.falApiKey || realtimeConfig.apiKey || "").trim() || fallbackKey, } as FalRealtimeConfig; } async function handleProxy(request: Request) { const session = await auth(); if (!session?.user?.id) { return Response.json({ code: "UNAUTHORIZED", message: "请先登录" }, { status: 401 }); } const targetUrl = request.headers.get("x-fal-target-url"); if (!targetUrl) { return Response.json( { code: "INVALID_TARGET", message: "缺少 x-fal-target-url 请求头" }, { status: 400 } ); } let parsedUrl: URL; try { parsedUrl = new URL(targetUrl); } catch { return Response.json( { code: "INVALID_TARGET", message: "x-fal-target-url 不是合法 URL" }, { status: 400 } ); } if (!ALLOWED_FAL_HOSTS.has(parsedUrl.host)) { return Response.json( { code: "TARGET_NOT_ALLOWED", message: "不允许代理该目标地址" }, { status: 403 } ); } const realtimeConfig = await getRealtimeCanvasConfig(); if (realtimeConfig.enabled === false) { return Response.json( { code: "FEATURE_DISABLED", message: "实时画布服务未启用(请在本地AI设置中开启)", }, { status: 403 } ); } const falApiKey = realtimeConfig.falApiKey?.trim() || ""; if (!falApiKey) { return Response.json( { code: "FAL_KEY_MISSING", message: "未配置 FAL API Key(请在本地AI设置的实时画布配置中填写)", }, { status: 500 } ); } let body: ArrayBuffer | undefined; if (request.method !== "GET" && request.method !== "HEAD") { body = await request.arrayBuffer(); } const headers = new Headers(); headers.set("Authorization", `Key ${falApiKey}`); headers.set("Accept", request.headers.get("accept") || "application/json"); const contentType = request.headers.get("content-type"); if (contentType) { headers.set("Content-Type", contentType); } const upstreamResponse = await fetch(parsedUrl.toString(), { method: request.method, headers, body, cache: "no-store", }); return new Response(upstreamResponse.body, { status: upstreamResponse.status, statusText: upstreamResponse.statusText, headers: upstreamResponse.headers, }); } export async function GET(request: Request) { return handleProxy(request); } export async function POST(request: Request) { return handleProxy(request); } export async function PUT(request: Request) { return handleProxy(request); } export async function PATCH(request: Request) { return handleProxy(request); } export async function DELETE(request: Request) { return handleProxy(request); }