67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { timingSafeEqual } from "node:crypto";
|
|
import { NextResponse } from "next/server";
|
|
import { auth } from "@/app/(auth)/auth";
|
|
import { getKittyCadProviderConfig } from "@/lib/kittycad/server";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
export const runtime = "nodejs";
|
|
|
|
const ALLOWED_WEBSOCKET_PATHS = new Set([
|
|
"/api/ai-cad/zoo-ws/ml/copilot",
|
|
"/api/ai-cad/zoo-ws/modeling/commands",
|
|
]);
|
|
|
|
function hasValidInternalSecret(request: Request) {
|
|
const expected = String(process.env.AI_CAD_WS_PROXY_SECRET || "").trim();
|
|
const received = String(
|
|
request.headers.get("x-hicad-ws-proxy-secret") || ""
|
|
).trim();
|
|
if (!expected || !received) {
|
|
return false;
|
|
}
|
|
|
|
const expectedBuffer = Buffer.from(expected);
|
|
const receivedBuffer = Buffer.from(received);
|
|
return (
|
|
expectedBuffer.length === receivedBuffer.length &&
|
|
timingSafeEqual(expectedBuffer, receivedBuffer)
|
|
);
|
|
}
|
|
|
|
export async function GET(request: Request) {
|
|
if (!hasValidInternalSecret(request)) {
|
|
return NextResponse.json({ error: "禁止访问" }, { status: 403 });
|
|
}
|
|
|
|
const originalUri = String(
|
|
request.headers.get("x-hicad-original-uri") || ""
|
|
).split("?", 1)[0];
|
|
if (!ALLOWED_WEBSOCKET_PATHS.has(originalUri)) {
|
|
return NextResponse.json({ error: "不支持的 Zoo WebSocket" }, { status: 403 });
|
|
}
|
|
|
|
const session = await auth();
|
|
if (!session?.user?.id) {
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
}
|
|
|
|
try {
|
|
const config = await getKittyCadProviderConfig();
|
|
return new Response(null, {
|
|
headers: {
|
|
"Cache-Control": "private, no-store",
|
|
"X-Zoo-Authorization": `Bearer ${config.apiKey}`,
|
|
},
|
|
status: 204,
|
|
});
|
|
} catch (error) {
|
|
return NextResponse.json(
|
|
{
|
|
error:
|
|
error instanceof Error ? error.message : "Zoo WebSocket 鉴权失败",
|
|
},
|
|
{ status: 503 }
|
|
);
|
|
}
|
|
}
|