125 lines
5.3 KiB
TypeScript
125 lines
5.3 KiB
TypeScript
/**
|
||
* # 脚本 导入页脚 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();
|