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).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(); 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 } ); } }