96 lines
3.5 KiB
TypeScript
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 });
|
|
}
|
|
}
|