63 lines
3.0 KiB
TypeScript
63 lines
3.0 KiB
TypeScript
/**
|
||
* # 脚本 同步文章分类成员(dump 快照 → 原站当前状态)
|
||
*
|
||
* 背景:/blog/ 归档本地 302 篇(26 页)vs 原站当前 7 页——dump 之后原站调整了
|
||
* Blog/Gateway 等分类的成员。本脚本按原站 REST API 的 categories 重建本地
|
||
* content_terms 分类关联(仅调整 REST 返回的文章;本地有而原站已删除的不动)。
|
||
*
|
||
* 用法:bun scripts/import-live-terms.ts [--dry]
|
||
*/
|
||
import { SQL } from "bun";
|
||
|
||
const LIVE = "https://www.dusuniot.com";
|
||
const sql = new SQL(process.env.DATABASE_URL || "postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot", { max: 1 });
|
||
const dry = process.argv.includes("--dry");
|
||
|
||
/** 本地分类 legacy_id → id(WP term id 与 legacy_id 一致) */
|
||
const catRows = await sql`select id, legacy_id, slug from categories where taxonomy = 'category' and legacy_id is not null`;
|
||
const catByLegacy = new Map<number, number>(catRows.map((r: any) => [Number(r.legacy_id), r.id as number]));
|
||
|
||
/** 本地已发布文章 legacy_id → content id */
|
||
const posts = await sql`select id, legacy_id from contents where type = 'post' and status = 'published' and legacy_id is not null`;
|
||
const contentByLegacy = new Map<number, number>(posts.map((r: any) => [Number(r.legacy_id), r.id as number]));
|
||
console.log("本地已发布文章:", posts.length);
|
||
|
||
/** 逐页拉原站文章的分类(_fields 精简返回) */
|
||
const liveCats = new Map<number, number[]>(); // wp post id → term ids
|
||
let page = 1;
|
||
while (true) {
|
||
const res = await fetch(`${LIVE}/wp-json/wp/v2/posts?per_page=100&page=${page}&_fields=id,categories`, { headers: { "User-Agent": "Mozilla/5.0" } });
|
||
if (!res.ok) {
|
||
if (res.status === 400) break; // 翻页越界
|
||
console.log("REST", res.status, "停止");
|
||
break;
|
||
}
|
||
const items = (await res.json()) as { id: number; categories: number[] }[];
|
||
for (const it of items) liveCats.set(it.id, it.categories || []);
|
||
if (items.length < 100) break;
|
||
page++;
|
||
await new Promise((r) => setTimeout(r, 400)); // 限速(429 防护)
|
||
}
|
||
console.log("原站文章:", liveCats.size);
|
||
|
||
let added = 0, removed = 0;
|
||
for (const [wpId, cats] of liveCats) {
|
||
const cid = contentByLegacy.get(wpId);
|
||
if (!cid) continue;
|
||
const localRows = await sql`select category_id from content_terms where content_id = ${Number(cid)}`;
|
||
const localSet = new Set<number>(localRows.map((r: any) => r.category_id as number));
|
||
const liveSet = new Set(cats.map((t) => catByLegacy.get(t)).filter((x): x is number => x != null));
|
||
for (const add of liveSet) {
|
||
if (localSet.has(add)) continue;
|
||
added++;
|
||
if (!dry) await sql`insert into content_terms (content_id, category_id) values (${cid}, ${Number(add)}) on conflict do nothing`;
|
||
}
|
||
for (const rm of localSet) {
|
||
if (liveSet.has(rm)) continue;
|
||
removed++;
|
||
if (!dry) await sql`delete from content_terms where content_id = ${cid} and category_id = ${Number(rm)}`;
|
||
}
|
||
}
|
||
console.log(`分类成员同步: 新增 ${added} 移除 ${removed}${dry ? "(dry 未写库)" : ""}`);
|
||
await sql.end();
|