v0.11.1: 权限体系重构 + AI管线优化 + 内容整理
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user