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

134 lines
3.8 KiB
TypeScript

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireAdmin } from "@/lib/admin-auth";
import { fetchLogoWithMethod } from "@/lib/logo-fetcher";
import { downloadLogo } from "@/lib/logo-storage";
export async function POST(request: Request) {
const authError = await requireAdmin();
if (authError) return authError;
try {
const body = await request.json();
const { type } = body as { type: "published" | "pending" };
if (!type || !["published", "pending"].includes(type)) {
return NextResponse.json(
{ error: "type 参数无效,必须为 published 或 pending" },
{ status: 400 }
);
}
const tools =
type === "published"
? await prisma.tool.findMany({
select: { id: true, name: true, websiteUrl: true, logoUrl: true },
})
: await prisma.pendingTool.findMany({
select: { id: true, name: true, websiteUrl: true, logoUrl: true },
});
const results: {
id: number;
name: string;
url: string;
accessible: boolean;
logoFetched: boolean;
logoMethod: string;
logoUrl: string;
}[] = [];
for (const tool of tools) {
const url = tool.websiteUrl;
let accessible = false;
let html: string | null = null;
if (url) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const res = await fetch(url, {
method: "GET",
signal: controller.signal,
redirect: "follow",
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
},
});
clearTimeout(timeout);
accessible = res.status >= 200 && res.status < 400;
if (accessible) {
const ct = res.headers.get("content-type") || "";
if (ct.includes("text/html")) {
html = await res.text();
}
}
} catch {
accessible = false;
}
}
let logoFetched = false;
let logoMethod = "";
let logoUrl = tool.logoUrl || "";
const needsLogo = !logoUrl || logoUrl === "" || (!logoUrl.startsWith("/logos/") && !logoUrl.startsWith("data:"));
if (accessible && url && needsLogo) {
try {
let logoResult = { url: "", method: "" };
if (html) {
logoResult = await fetchLogoWithMethod(url, html);
}
if (!logoResult.url) {
logoResult = await fetchLogoWithMethod(url);
}
if (logoResult.url) {
const localPath = await downloadLogo(logoResult.url, tool.name.replace(/[^a-z0-9]/gi, "_").substring(0, 20));
if (type === "published") {
await prisma.tool.update({
where: { id: tool.id },
data: { logoUrl: localPath },
});
} else {
await prisma.pendingTool.update({
where: { id: tool.id },
data: { logoUrl: localPath },
});
}
logoFetched = true;
logoMethod = logoResult.method;
logoUrl = localPath;
}
} catch {
// Logo fetch failed silently
}
}
results.push({
id: tool.id,
name: tool.name,
url: url || "",
accessible,
logoFetched,
logoMethod,
logoUrl,
});
}
return NextResponse.json({ results });
} catch (error: unknown) {
const err = error as { message?: string };
return NextResponse.json({ error: err.message }, { status: 500 });
}
}