import { z } from "zod"; import { getAiProviderById } from "@/lib/db/queries"; import { ChatSDKError } from "@/lib/errors"; import { KittyCadApiError, kittyCadRequest } from "@/lib/kittycad/client"; import { getKittyCadProviderConfig } from "@/lib/kittycad/server"; import { requireAdmin } from "@/lib/middleware/admin"; export const runtime = "nodejs"; const requestSchema = z.object({ config: z .object({ providerId: z.string().uuid().optional(), name: z.string().trim().optional(), baseUrl: z.string().trim().optional(), apiKey: z.string().trim().optional(), }) .optional(), }); function pickDisplayName(user: Record) { const candidates = [ user.name, user.full_name, user.display_name, user.username, user.first_name, ]; for (const value of candidates) { if (typeof value === "string" && value.trim()) { return value.trim(); } } return null; } function pickEmail(user: Record) { const candidates = [user.email, user.primary_email]; for (const value of candidates) { if (typeof value === "string" && value.trim()) { return value.trim(); } } return null; } async function resolveKittyCadConfig( input?: { providerId?: string; name?: string; baseUrl?: string; apiKey?: string; } | null ) { const providerId = String(input?.providerId || "").trim(); const inputApiKey = String(input?.apiKey || "").trim(); const inputBaseUrl = String(input?.baseUrl || "").trim(); const inputName = String(input?.name || "").trim(); if (providerId) { const savedProvider = await getAiProviderById({ id: providerId }); if (!savedProvider) { throw new Error("未找到对应的 KittyCAD CAD 提供商"); } return { apiKey: inputApiKey || String(savedProvider.apiKey || "").trim(), baseUrl: inputBaseUrl || String(savedProvider.baseUrl || "").trim(), providerId: savedProvider.id, providerName: inputName || savedProvider.name || "KittyCAD CAD", source: "provider" as const, }; } if (inputApiKey) { return { apiKey: inputApiKey, baseUrl: inputBaseUrl || "https://api.zoo.dev", providerName: inputName || "KittyCAD CAD", source: "provider" as const, }; } return await getKittyCadProviderConfig(); } export async function POST(request: Request) { try { await requireAdmin(); const body = requestSchema.parse(await request.json().catch(() => ({}))); const config = await resolveKittyCadConfig(body.config); if (!config.apiKey.trim()) { throw new Error("请填写 KittyCAD API Token"); } const startedAt = Date.now(); const user = await kittyCadRequest>(config, { method: "GET", path: "/user", }); const latencyMs = Date.now() - startedAt; return Response.json({ success: true, providerName: config.providerName || "KittyCAD CAD", baseUrl: config.baseUrl, latencyMs, userId: typeof user.id === "string" && user.id.trim() ? user.id.trim() : null, userName: pickDisplayName(user), userEmail: pickEmail(user), }); } catch (error) { if (error instanceof ChatSDKError) { return error.toResponse(); } if (error instanceof KittyCadApiError) { return Response.json( { code: "bad_request:api", message: error.message, payload: error.payload, }, { status: error.httpStatus || 400 } ); } if (error instanceof z.ZodError) { return new ChatSDKError("bad_request:api", error.message).toResponse(); } return Response.json( { code: "bad_request:api", message: error instanceof Error ? error.message : "KittyCAD 连接测试失败", }, { status: 400 } ); } }