Files
dusuniot/scripts/import-page-css.ts
T

100 lines
3.9 KiB
TypeScript
Raw 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.
/**
* # 脚本 导入原站页面级生成 CSS(post-<legacy_id>.css)到 contents.meta.page_css
*
* 背景:DB dump 的 meta.elementor 缺少 widget 级 custom_css(如 EAEL infobox 的
* `.elementor-element-7f1dc73 .eael-infobox .infobox-icon img{width:80px}`),这些规则
* 只存在于原站 Elementor 生成的页面样式表 post-<post_id>.css 中。
*
* 做法:
* 1. 抓 https://www.dusuniot.com/wp-content/uploads/elementor/css/post-<legacy_id>.css
* 2. 只保留选择器含 .elementor-element-<id>(id 属于本文档)的规则(含 @media 块内),
* 把 `.elementor-element.elementor-element-<id>` / `.elementor-element-<id>` 改写为
* `[data-el="<id>"]`(与渲染器 DOM 对应),剥掉 `.elementor-<pid>` 页面作用域前缀
* 3. 拼接后写回 meta.page_css,渲染器(ElementorRenderer pageCss prop)注入 <style>
*
* 用法:bun scripts/import-page-css.ts <slug> [<slug> ...] (缺省 = 全部方案落地页)
*/
import { SQL } from "bun";
import { collectDocIds, filterCssForIds } from "../src/utils/css-translate";
/** # 常量 方案落地页 slug 全集(缺省导入目标) **/
const SOLUTION_SLUGS = [
"smart-apartment-gateway",
"smart-apartment-gateway-oem",
"oil-gas-management-solution",
"energy-storage-ems-solution",
"smart-robot-controller-solution",
"vehicle-iot-solution",
"smart-building-solution",
"smart-retail-solution",
"smart-home-solution",
"smart-apartment-solution",
"smart-elderly-care-solution",
"ble-positioning-solution",
"ble-mesh-lighting-control-solution",
"home-assistant-grow-lighting-solution",
];
/** # 常量 原站页面 CSS 地址模板 **/
const CSS_URL = (pid: number) => `https://www.dusuniot.com/wp-content/uploads/elementor/css/post-${pid}.css`;
const sql0 = new SQL("postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot", { max: 1 });
// CONTENT_TYPE 环境变量支持产品(product)等其它内容类型;"-all" = 该类型全部已发布内容
const contentType = process.env.CONTENT_TYPE || "post";
let slugs = process.argv.slice(2).length ? process.argv.slice(2) : SOLUTION_SLUGS;
if (slugs.length === 1 && slugs[0] === "-all") {
const rows0 = await sql0`select slug from contents where type = ${contentType} and status = ${"published"}`;
slugs = rows0.map((r: any) => r.slug);
}
await sql0.end();
const sql = new SQL("postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot", { max: 1 });
/** 已导入过的跳过(断点续传;meta.page_css 键存在即视为完成) */
let done = 0;
let skipped = 0;
for (const slug of slugs) {
const rows = await sql`select id, legacy_id, meta::text as meta from contents where slug = ${slug} and type = ${contentType} limit 1`;
const row = rows[0];
if (!row) {
skipped++;
continue;
}
const meta = JSON.parse(row.meta);
if (meta.page_css !== undefined) {
skipped++;
continue;
}
const ids = collectDocIds(meta.elementor);
const pid = row.legacy_id;
if (!pid) {
skipped++;
continue;
}
// 重试 3 次、间隔 300ms(原站偶发 ECONNRESET/限流)
let raw: string | null = null;
for (let attempt = 0; attempt < 3 && raw === null; attempt++) {
try {
const res = await fetch(CSS_URL(pid), { headers: { "User-Agent": "Mozilla/5.0" } });
if (res.ok) raw = await res.text();
else if (res.status === 404) break;
} catch {
await new Promise((r) => setTimeout(r, 300));
}
}
if (raw === null) {
console.log("SKIP(fetch失败):", slug, pid);
skipped++;
continue;
}
const pageCss = filterCssForIds(raw, ids, pid);
meta.page_css = pageCss;
// ::text::jsonb 双重转换:Bun SQL 绑定字符串直接 ::jsonb 会把整串存成 JSON 字符串值
await sql`update contents set meta = ${JSON.stringify(meta)}::text::jsonb where id = ${row.id}`;
done++;
if (done % 50 === 0) console.log(`... ${done} imported`);
}
console.log("imported:", done, "skipped:", skipped);
await sql.end();