Files
zhuiguang-ai/src/app/api/admin/discover-tools/route.ts
T

96 lines
3.5 KiB
TypeScript

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireAdmin } from "@/lib/admin-auth";
import { toolImportSchema } from "@/lib/validation";
export async function POST(request: Request) {
const authError = await requireAdmin();
if (authError) return authError;
try {
const body = await request.json();
const parsed = toolImportSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json({ error: parsed.error.issues[0].message }, { status: 400 });
}
const { tools, categoryId } = parsed.data;
const [existingPending, existingPublished] = await Promise.all([
prisma.pendingTool.findMany({ select: { name: true, websiteUrl: true } }),
prisma.tool.findMany({ select: { name: true, websiteUrl: true } }),
]);
const allExisting = [...existingPending, ...existingPublished];
const nameSet = new Set(allExisting.map((t) => t.name));
const urlSet = new Set(allExisting.map((t) => t.websiteUrl).filter(Boolean) as string[]);
const existingSlugs = await prisma.pendingTool.findMany({ select: { slug: true } });
const slugSet = new Set(existingSlugs.map((s) => s.slug));
const toCreate: { name: string; slug: string; description: string | null; websiteUrl: string | null; logoUrl: string | null; categoryId: number | null; pricingModel: "free" | "freemium" | "paid" | "subscription"; pricingDetail: string | null; features: any; tags: any; sourceUrl: string }[] = [];
const results: { name: string; status: string }[] = [];
for (const tool of tools) {
const name = tool.name || "";
if (!name || nameSet.has(name)) {
results.push({ name: name || "未知", status: nameSet.has(name) ? "跳过-名称重复" : "跳过-名称为空" });
continue;
}
const websiteUrl = tool.websiteUrl || "";
if (websiteUrl && urlSet.has(websiteUrl)) {
results.push({ name, status: "跳过-网址重复" });
continue;
}
let slug = name
.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-")
.replace(/^-|-$/g, "");
if (!slug || /^[\u4e00-\u9fff-]+$/.test(slug)) {
slug = "tool-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
}
if (slugSet.has(slug)) {
slug = slug + "-" + Math.floor(Math.random() * 10000);
}
slugSet.add(slug);
toCreate.push({
name,
slug,
description: tool.description || null,
websiteUrl: websiteUrl || null,
logoUrl: tool.logoUrl || null,
categoryId,
pricingModel: (tool.pricingModel as "free" | "freemium" | "paid" | "subscription") || "freemium",
pricingDetail: tool.pricingDetail || null,
features: tool.features || null,
tags: tool.tags || null,
sourceUrl: "AI发现",
});
nameSet.add(name);
if (websiteUrl) urlSet.add(websiteUrl);
results.push({ name, status: "已导入" });
}
if (toCreate.length > 0) {
await prisma.$transaction(
toCreate.map((data) =>
prisma.pendingTool.create({ data })
)
);
}
return NextResponse.json({
success: true,
total: tools.length,
imported: results.filter((r) => r.status === "已导入").length,
skipped: results.filter((r) => r.status !== "已导入").length,
details: results,
});
} catch (error: unknown) {
const err = error as { message?: string };
return NextResponse.json({ error: err.message }, { status: 500 });
}
}