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 } ); } }