Files
yingfungsolar/scripts/setup.mjs
T
philip-cat 6dedb5469f Move seed.sql out of migrations and add SEO structured data
- Relocate seed.sql to scripts/ so wrangler d1 migrations apply
  doesn't treat it as a migration; update package.json, setup.mjs,
  README, and all AGENTS.md references.
- Add src/lib/schema-org.ts builders (LocalBusiness, FAQPage,
  BlogPosting, BreadcrumbList) and emit JSON-LD via Base.astro.
- Add lastmod to sitemap entries.
- Set real site domain, worker route, and D1/KV ids.
- Improve image loading hints and heading semantics across site
  components; switch BlogIndex to client:idle.
2026-09-13 16:21:59 +08:00

177 lines
6.2 KiB
JavaScript
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.
#!/usr/bin/env node
import { execSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
import { fileURLToPath } from "node:url";
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
const ROOT = dirname(SCRIPT_DIR);
const D1_PLACEHOLDER = "PASTE_D1_DATABASE_ID_HERE";
const KV_PLACEHOLDER = "PASTE_KV_NAMESPACE_ID_HERE";
const R2_BUCKET = "yingfung-solar-media";
const WRANGLER = join(ROOT, "wrangler.jsonc");
const SEED_FILE = join(ROOT, "scripts", "seed.sql");
const ASTRO_CONFIG = join(ROOT, "astro.config.mjs");
const DB_NAME = "yingfung-solar-db";
const rl = createInterface({ input, output });
function run(cmd) {
return execSync(cmd, { encoding: "utf8", stdio: ["inherit", "pipe", "inherit"] });
}
function runSoft(cmd) {
try {
return { ok: true, out: run(cmd), err: "" };
} catch (err) {
return { ok: false, out: err.stdout ?? err.stderr ?? "", err: err.stderr ?? "" };
}
}
/** 由 wrangler 輸出抽 id;同時支援 JSON"key": "value")同 TOMLkey = "value")。 */
function extractId(out, keys) {
for (const key of keys) {
const re = new RegExp(`(?<!\\w)"?${key}"?\\s*[=:]\\s*"([0-9a-f-]+)"`, "i");
const m = out.match(re);
if (m) return m[1];
}
return null;
}
/** 由 wrangler 嘅 JSON 陣列輸出搵第一個符合條件嘅項目。 */
function findInJsonArray(text, predicate) {
try {
const arr = JSON.parse(text);
if (Array.isArray(arr)) return arr.find(predicate) ?? null;
} catch {
// 唔係 JSON 就當搵唔到。
}
return null;
}
async function main() {
console.log("\n=== 盈豐太陽能 — Cloudflare 一鍵設定 ===\n");
console.log("檢查 wrangler 登入狀態…");
const who = runSoft("npx wrangler whoami --json");
const whoText = `${who.out}\n${who.err}`;
if (!who.ok || /not authenticated|not logged in/i.test(whoText)) {
console.error("未登入 Cloudflare。請先執行: npm run login");
process.exit(1);
}
let config = readFileSync(WRANGLER, "utf8");
if (config.includes(D1_PLACEHOLDER)) {
console.log(`建立 D1 database「${DB_NAME}」…`);
const res = runSoft(`npx wrangler d1 create ${DB_NAME}`);
let out = res.out || res.err || "";
let id = res.ok ? extractId(out, ["database_id"]) : null;
if (!res.ok) {
console.log(" 建立失敗,可能已經存在;嘗試查出現有 database_id…");
const list = runSoft("npx wrangler d1 list --json");
out = `${out}\n${list.out || list.err || ""}`;
const found = findInJsonArray(list.out, (r) => r.name === DB_NAME);
id = found?.uuid ?? null;
}
if (!id) {
console.error("建立 / 查詢 D1 database 失敗。以下係 wrangler 輸出:");
console.error(out);
process.exit(1);
}
config = config.replace(D1_PLACEHOLDER, () => id);
writeFileSync(WRANGLER, config, "utf8");
console.log(` database_id = ${id}(已寫入 wrangler.jsonc`);
} else {
console.log("D1 id 已設定,略過。");
}
if (config.includes(KV_PLACEHOLDER)) {
console.log("建立 KV namespace「CACHE」…");
const res = runSoft("npx wrangler kv namespace create CACHE");
let out = res.out || res.err || "";
let id = res.ok ? extractId(out, ["id"]) : null;
if (!res.ok) {
console.log(" 建立失敗,可能已經存在;嘗試查出現有 KV id…");
const list = runSoft("npx wrangler kv namespace list");
out = `${out}\n${list.out || list.err || ""}`;
const found = findInJsonArray(list.out, (r) => r.title === "CACHE");
id = found?.id ?? null;
}
if (!id) {
console.error("建立 / 查詢 KV namespace 失敗。以下係 wrangler 輸出:");
console.error(out);
process.exit(1);
}
config = config.replace(KV_PLACEHOLDER, () => id);
writeFileSync(WRANGLER, config, "utf8");
console.log(` kv id = ${id}(已寫入 wrangler.jsonc`);
} else {
console.log("KV id 已設定,略過。");
}
console.log(`檢查 / 建立 R2 bucket「${R2_BUCKET}」…`);
const r2List = runSoft("npx wrangler r2 bucket list");
const r2Text = `${r2List.out || ""}\n${r2List.err || ""}`;
if (new RegExp(`\\b${R2_BUCKET}\\b`).test(r2Text)) {
console.log(" R2 bucket 已存在,略過。");
} else {
const r2 = runSoft(`npx wrangler r2 bucket create ${R2_BUCKET}`);
if (r2.ok) {
console.log(" R2 bucket 已建立。");
} else {
console.log(" 建立 R2 bucket 失敗(可能未開通 R2),請稍後手動執行: npm run r2:create");
console.log(r2.out || r2.err || "");
}
}
const setPassword = await rl.question("而家設定後台密碼 ADMIN_PASSWORD(y/N)");
if (setPassword.trim().toLowerCase() === "y") {
execSync("npx wrangler secret put ADMIN_PASSWORD", { stdio: "inherit" });
console.log("ADMIN_PASSWORD 已處理。");
} else {
console.log("略過 ADMIN_PASSWORD。");
}
const migrate = await rl.question("而家套用雲端 migration + seed(y/N)");
if (migrate.trim().toLowerCase() === "y") {
execSync(`npx wrangler d1 migrations apply ${DB_NAME} --remote`, { stdio: "inherit" });
execSync(`npx wrangler d1 execute ${DB_NAME} --remote --file="${SEED_FILE}"`, { stdio: "inherit" });
}
let siteReady = true;
try {
if (readFileSync(ASTRO_CONFIG, "utf8").includes("https://example.com")) {
siteReady = false;
console.log("\n⚠️ 警告:astro.config.mjs 嘅 site 仍然係 https://example.com。");
console.log(" 上線前必須改成真域名,否則 sitemap / canonical / OG 會出錯。");
console.log(" 已跳過 build + deploy;更新 site 後請自行執行: npm run deploy\n");
}
} catch {
// 讀唔到 astro.config.mjs 就當冇問題,繼續。
}
if (siteReady) {
const deploy = await rl.question("而家 build + deploy(y/N)");
if (deploy.trim().toLowerCase() === "y") {
execSync("npm run build", { stdio: "inherit" });
execSync("npx wrangler deploy", { stdio: "inherit" });
}
}
console.log("\n完成。\n");
rl.close();
}
main().catch((err) => {
console.error(err);
process.exit(1);
});