修复若干问题
This commit is contained in:
1 parent
c8cdf9a7bc
commit
bf083523c8
12 files changed
+581
-40
No files matched your search
@@ -0,0 +1,104 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { ARTALK_SERVER, ARTALK_SITE } from "@/lib/config";
|
||||
|
||||
async function getArtalkData(path: string, token: string) {
|
||||
const url = `${ARTALK_SERVER}${path}`;
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Artalk API error: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function deleteArtalkPage(id: number, token: string) {
|
||||
const url = `${ARTALK_SERVER}/api/v2/pages/${id}`;
|
||||
const res = await fetch(url, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ site_name: ARTALK_SITE }),
|
||||
});
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json().catch(() => ({}));
|
||||
const token = (body as Record<string, string>).token;
|
||||
if (!token) {
|
||||
return NextResponse.json({ error: "token required" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 1. Login to get a valid token
|
||||
const allPages: { id: number; key: string; title: string; comment_count?: number }[] = [];
|
||||
let offset = 0;
|
||||
const limit = 200;
|
||||
|
||||
// Fetch all pages
|
||||
while (true) {
|
||||
const data = await getArtalkData(
|
||||
`/api/v2/pages?site_name=${encodeURIComponent(ARTALK_SITE)}&limit=${limit}&offset=${offset}`,
|
||||
token
|
||||
);
|
||||
const pages = data.pages || data.data || [];
|
||||
if (pages.length === 0) break;
|
||||
allPages.push(...pages);
|
||||
offset += limit;
|
||||
if (pages.length < limit) break;
|
||||
}
|
||||
|
||||
// 2. Group by title
|
||||
const byTitle = new Map<string, typeof allPages>();
|
||||
for (const p of allPages) {
|
||||
const t = (p.title || "").trim();
|
||||
if (!byTitle.has(t)) byTitle.set(t, []);
|
||||
byTitle.get(t)!.push(p);
|
||||
}
|
||||
|
||||
// 3. For each duplicate group, keep the one with most comments, delete the rest (especially 0-comment ones)
|
||||
let deleted = 0;
|
||||
const deletedIds: number[] = [];
|
||||
|
||||
for (const [title, pages] of byTitle) {
|
||||
if (pages.length <= 1) continue;
|
||||
|
||||
// Sort: most comments first, then by id desc (newest first)
|
||||
pages.sort((a, b) => {
|
||||
const ca = a.comment_count ?? 0;
|
||||
const cb = b.comment_count ?? 0;
|
||||
if (ca !== cb) return cb - ca;
|
||||
return b.id - a.id;
|
||||
});
|
||||
|
||||
// Keep the first one, delete the rest
|
||||
const [keep, ...toDelete] = pages;
|
||||
for (const p of toDelete) {
|
||||
try {
|
||||
const ok = await deleteArtalkPage(p.id, token);
|
||||
if (ok) {
|
||||
deleted++;
|
||||
deletedIds.push(p.id);
|
||||
}
|
||||
} catch { /* skip failed deletions */ }
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Also clean up pages with 0 comments that are NOT part of duplicate groups
|
||||
// (only if they look like test/404 pages — very short titles or empty keys)
|
||||
// Skip this step to be safe — only deduplicate.
|
||||
|
||||
return NextResponse.json({
|
||||
total: allPages.length,
|
||||
duplicateGroups: [...byTitle.values()].filter((ps) => ps.length > 1).length,
|
||||
deleted,
|
||||
deletedIds,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Unknown error" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user