From 125030ce2ebd823ded5692c73fc17ceed022ccd7 Mon Sep 17 00:00:00 2001 From: philipcheung Date: Sat, 12 Sep 2026 09:54:40 +0800 Subject: [PATCH] Add R2 MEDIA binding and setup script --- package.json | 1 + scripts/setup.mjs | 16 ++++++++++++++++ src/lib/env.ts | 1 + worker-configuration.d.ts | 8 +++++--- wrangler.jsonc | 6 ++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 39eef9a..f0fa4b3 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "db:seed:local": "wrangler d1 execute yingfung-solar-db --local --file=./migrations/seed.sql", "db:studio": "drizzle-kit studio", "kv:create": "wrangler kv namespace create CACHE", + "r2:create": "wrangler r2 bucket create yingfung-solar-media", "secret": "wrangler secret put ADMIN_PASSWORD", "types": "wrangler types", "setup": "node scripts/setup.mjs" diff --git a/scripts/setup.mjs b/scripts/setup.mjs index f60b1db..59cd8f0 100644 --- a/scripts/setup.mjs +++ b/scripts/setup.mjs @@ -11,6 +11,7 @@ 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"); @@ -116,6 +117,21 @@ async function main() { 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" }); diff --git a/src/lib/env.ts b/src/lib/env.ts index 4cecc96..3ab42fe 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -3,6 +3,7 @@ import { env } from "cloudflare:workers"; export type AppEnv = { DB: D1Database; CACHE: KVNamespace; + MEDIA: R2Bucket; ADMIN_PASSWORD?: string; AI_API_KEY?: string; TAVILY_API_KEY?: string; diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 6903b67..556fd55 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,11 +1,13 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: bcadd21c7ffb76583873a6eda250f922) +// Generated by Wrangler by running `wrangler types` (hash: 03ae7c4bd9c0facc561b340619a53d21) // Runtime types generated with workerd@1.20260908.1 2026-09-10 nodejs_compat interface __BaseEnv_Env { CACHE: KVNamespace; + MEDIA: R2Bucket; DB: D1Database; ASSETS: Fetcher; - SITE_NAME: "你的公司名"; + SITE_NAME: "盈豐太陽能"; + ADMIN_PASSWORD: string; } declare namespace Cloudflare { interface Env extends __BaseEnv_Env {} @@ -15,7 +17,7 @@ type StringifyValues> = { [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; }; declare namespace NodeJS { - interface ProcessEnv extends StringifyValues> {} + interface ProcessEnv extends StringifyValues> {} } // Begin runtime types diff --git a/wrangler.jsonc b/wrangler.jsonc index 1731518..ec9cc43 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -33,5 +33,11 @@ "binding": "CACHE", "id": "PASTE_KV_NAMESPACE_ID_HERE" } + ], + "r2_buckets": [ + { + "binding": "MEDIA", + "bucket_name": "yingfung-solar-media" + } ] }