Files
dusuniot/scripts/import-footer-template.ts

125 lines
5.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* # 脚本 导入页脚 Elementor 模板(legacy elementor_library post 58)
* 背景:WordPress 导入管线未迁移 elementor_library 类型(仅 page/post/landing),
* 页脚模板(post 58,含 46d7afa / d834160 两节及其内嵌 28c7f0b8 / a428bb4)因此缺失。
* 数据源:legacy/dusuniot-db-20261029.sql 中 `(meta_id,58,'_elementor_data','…')` 行。
* 补丁:线上页脚另有 f95e22e(Google Preferred Source 图,dump 快照之后新增),
* 依线上 DOM(/tmp/orig-footer.html 取证:class elementor-widget__width-inherit、
* img 528×64、链接 google.com/preferences/source?q=dusuniot.com)补入 2ccb47d 列末位。
* 结果写入 contents(type=template, slug=footer, meta.elementor),供 Layout 渲染。
* 运行:bun scripts/import-footer-template.ts
*/
import { SQL } from "bun";
const DUMP = "/home/bigcode/data/dev/sites/dusuniot/legacy/dusuniot-db-20261029.sql";
const db = new SQL("postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot");
/** # 函数 从 dump 提取 post 58 的 _elementor_data 并反转义为 JSON
* 返回值 Elementor 元素数组
*/
async function extractTemplate(): Promise<Record<string, unknown>[]> {
const text = await Bun.file(DUMP).text();
const m = text.match(/\(\d+,58,'_elementor_data','((?:[^'\\]|\\.)*)'\)/);
if (!m) throw new Error("dump 中未找到 post 58 的 _elementor_data");
const raw = m[1]!;
// SQL dump 反斜杠转义:\" → "、\' → '、\\ → \
const json = raw.replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
const data = JSON.parse(json);
if (!Array.isArray(data)) throw new Error("_elementor_data 不是数组");
return data as Record<string, unknown>[];
}
/** # 函数 深度查找指定 id 的元素节点 */
function findElement(els: Record<string, unknown>[], id: string): Record<string, unknown> | null {
for (const el of els) {
if (el.id === id) return el;
const children = el.elements as Record<string, unknown>[] | undefined;
if (children?.length) {
const hit = findElement(children, id);
if (hit) return hit;
}
}
return null;
}
/** # 函数 线上快照新增的 f95e22e(Google Preferred Source 图)
* 线上 post-58.css 取证:.elementor-element-f95e22e img{width:300px}、
* > .elementor-widget-container{margin:10px 0 0};dump 快照之后新增,故在此补全。
*/
function preferredSourceWidget(): Record<string, unknown> {
return {
id: "f95e22e",
elType: "widget",
settings: {
image: {
url: "http://www.dusuniot.com/wp-content/uploads/2022/06/google-preffered-source.png",
id: 161805,
alt: "google prefferd source",
source: "library",
size: "",
},
image_size: "full",
width: { unit: "px", size: 300, sizes: [] },
align: "left",
_element_width: "inherit",
_margin: { unit: "px", top: "10", right: "0", bottom: "0", left: "0", isLinked: false },
link: {
url: "https://www.google.com/preferences/source?q=dusuniot.com",
is_external: "on",
nofollow: "on",
custom_attributes: "",
},
},
elements: [],
widgetType: "image",
};
}
const elements = await extractTemplate();
// 补丁:2ccb47d 列末尾追加 f95e22e(幂等:已存在则跳过)
const col = findElement(elements, "2ccb47d");
if (!col) throw new Error("模板缺少 2ccb47d 列");
const colChildren = (col.elements as Record<string, unknown>[]) || [];
const psIdx = colChildren.findIndex((el) => el.id === "f95e22e");
if (psIdx >= 0) colChildren[psIdx] = preferredSourceWidget();
else colChildren.push(preferredSourceWidget());
col.elements = colChildren;
// 补丁:社交图标行 405d251 宽度模式(线上 class=elementor-widget__width-auto、
// 生成 CSS width:auto;dump 快照为 inherit,实测线上为 auto → 与 gtranslate 同排)
const social = findElement(elements, "405d251");
if (social) {
const st = (social.settings as Record<string, unknown>) || {};
st._element_width = "auto";
social.settings = st;
}
// 补丁:gtranslate 短代码控件 2481635 桌面外距(线上 post-58.css:margin -10px 0 0 20px;
// dump 只有 _margin_mobile,桌面值来自快照之后的编辑)
const gt = findElement(elements, "2481635");
if (gt) {
const st = (gt.settings as Record<string, unknown>) || {};
st._margin = { unit: "px", top: "-10", right: "0", bottom: "0", left: "20", isLinked: false };
gt.settings = st;
}
const meta = JSON.stringify({ elementor: elements });
// 写入 contents:type=template / slug=footer;已存在则更新
const existing = await db`SELECT id FROM contents WHERE slug='footer' AND type='template'`;
if (existing.length) {
await db`UPDATE contents SET meta=${meta}::text::jsonb, status='published', updated_at=now() WHERE id=${existing[0]!.id}`;
console.log("已更新 footer 模板,contents.id =", existing[0]!.id);
} else {
const inserted = await db`
INSERT INTO contents (type, slug, title, status, meta, legacy_id, published_at)
VALUES ('template', 'footer', 'Footer', 'published', ${meta}::text::jsonb, 58, now())
RETURNING id`;
console.log("已插入 footer 模板,contents.id =", inserted[0]!.id);
}
// 验证:节数量与顶层 id
const top = elements.map((el) => el.id).join(", ");
console.log("顶层节:", top);
await db.end();