/** * # 脚本 导航 404 数据修复(一次性,幂等) * 依据对原站 dusuniot.com 的逐 URL 探测结论: * 1. 发布落地页草稿(原站已 404 但内容在库、且被站内内容/重定向规则引用): * - contents.id=49 bluetooth-roaming-gateway(案例文章外链目标) * - contents.id=40 iot-hardware-developer(页脚菜单组外链目标) * - contents.id=53 ultra-convergence-solution(redirects 规则 491 的目标) * - contents.id=64 single-board-computer(博客文章外链目标) * 2. 修复导入内容里的畸形链接:href 中 URL 后又拼接了一次单斜杠版自身 * (…/product/x/https:/www.dusuniot.com/product/x/),还原为第一个 URL。 * 3. 补 redirects 表规则:目标多为「线上同样 404 的陈旧链接」,指向本地最近似存活页。 * 重复执行安全:草稿发布、内容修复与规则插入都先查再写。 */ import { SQL } from "bun"; const sql = new SQL(process.env.DATABASE_URL || "postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot"); // ── 1. 发布落地页草稿 ── for (const id of [40, 49, 53, 64]) { const [row] = await sql`SELECT id, slug, status FROM contents WHERE id = ${id}`; if (!row) { console.log(`contents.id=${id} 不存在,跳过`); continue; } if (row.status === "published") { console.log(`contents.id=${id} (${row.slug}) 已是 published`); continue; } await sql`UPDATE contents SET status = 'published' WHERE id = ${id}`; console.log(`已发布 contents.id=${id} (${row.slug})`); } // ── 2. 修复内容里畸形拼接的链接(URL 后跟单斜杠版自身) ── const bad = await sql`SELECT id, slug, html FROM contents WHERE html LIKE '%/https:/www.dusuniot.com%'`; const dupRe = /(https:\/\/www\.dusuniot\.com\/[^\s"'<>]+?)\/https:\/www\.dusuniot\.com\/[^\s"'<>]*/g; for (const row of bad) { const fixed = row.html.replace(dupRe, "$1"); if (fixed !== row.html) { await sql`UPDATE contents SET html = ${fixed} WHERE id = ${row.id}`; console.log(`已修复畸形链接: contents.id=${row.id} (${row.slug})`); } else { console.log(`contents.id=${row.id} (${row.slug}) 模式命中但无可替换(人工检查)`); } } // ── 3. 补充 redirects 规则(source 唯一先查后插) ── const RULES: { source: string; target: string; note: string }[] = [ // 原站已 404 的案例/新闻文:落对应归档(本站归档已可用) { source: "/case-study/tuya-smart-home-solution/", target: "/case-study/", note: "线上 404 的陈旧案例链接 → 案例归档" }, { source: "/news/a-multi-protocol-smart-home-mini-pc-hub/", target: "/news/", note: "线上 404 的陈旧新闻链接 → 新闻归档" }, // 线上已 404 的落地页 → 本地最近的存活落地页(odm/zigbee-gateways 均 200 且主题相近) { source: "/landing-pages/oem-odm/", target: "/landing-pages/odm/", note: "线上 404 → 本地 ODM 落地页" }, { source: "/landing-pages/tuya-router-gateway/", target: "/landing-pages/zigbee-gateways/", note: "线上 404 → 本地 Zigbee 网关落地页" }, { source: "/landing-pages/tuya-series-products/", target: "/landing-pages/zigbee-gateways/", note: "线上 404 → 本地 Zigbee 网关落地页" }, { source: "/landing-pages/tuya-smart-home-zigbee-gateway/", target: "/landing-pages/zigbee-gateways/", note: "线上 404 → 本地 Zigbee 网关落地页" }, // 原站 WooCommerce 属性归档(pa_type 筛选页),本站无该属性归档 → 商城列表 { source: "/shop/type-iot-gateway/", target: "/shop/", note: "线上 200 的属性归档,本站无此筛选 → 商城" }, { source: "/shop/type-som/", target: "/shop/", note: "线上 200 的属性归档,本站无此筛选 → 商城" }, { source: "/shop/downlink-z-wave-or-zigbee/", target: "/shop/", note: "线上 404 的属性归档 → 商城" }, // 原站 301 后终态 404 的规格页短链 → 产品列表(本地无对应产品) { source: "/dsgw-075-tuya-specification/", target: "/collection/", note: "线上 301→404 → 产品列表" }, // 原站 200 但内容未导入(Elementor 页抓取导入成本高,先落 About Us;后续可导入真页替换) { source: "/partners/", target: "/about-us/", note: "原站合作伙伴页未导入 → 关于我们" }, ]; for (const r of RULES) { const [dup] = await sql`SELECT id FROM redirects WHERE source = ${r.source}`; if (dup) { console.log(`规则已存在(${dup.id}): ${r.source}`); continue; } await sql`INSERT INTO redirects (source, target, code, is_regex, enabled) VALUES (${r.source}, ${r.target}, 301, false, true)`; console.log(`+ 301 ${r.source} → ${r.target} (${r.note})`); } console.log("完成"); process.exit(0);