#!/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, "migrations", "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")同 TOML(key = "value")。 */ function extractId(out, keys) { for (const key of keys) { const re = new RegExp(`(? 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); });