From db890fba0d2438cbfd6c42703cb2bda3be0e4c41 Mon Sep 17 00:00:00 2001 From: eoao <34203076+eoao@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:35:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20GitHub=20Actions=20?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Abner <22141172+Silentely@users.noreply.github.com> --- .github/workflows/deploy-cloudflare.yml | 117 ++++++++++++++++++++++++ README.md | 30 +++--- {demo => doc/demo}/demo1.png | Bin {demo => doc/demo}/demo2.png | Bin {demo => doc/demo}/demo3.png | Bin {demo => doc/demo}/demo4.png | Bin {demo => doc/demo}/demo5.png | Bin {demo => doc/demo}/demo6.png | Bin {demo => doc/demo}/demo7.png | Bin {demo => doc/demo}/demo8.png | Bin {demo => doc/demo}/logo.png | Bin doc/github-action.md | 38 ++++++++ mail-worker/wrangler-action.toml | 36 ++++++++ 13 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/deploy-cloudflare.yml rename {demo => doc/demo}/demo1.png (100%) rename {demo => doc/demo}/demo2.png (100%) rename {demo => doc/demo}/demo3.png (100%) rename {demo => doc/demo}/demo4.png (100%) rename {demo => doc/demo}/demo5.png (100%) rename {demo => doc/demo}/demo6.png (100%) rename {demo => doc/demo}/demo7.png (100%) rename {demo => doc/demo}/demo8.png (100%) rename {demo => doc/demo}/logo.png (100%) create mode 100644 doc/github-action.md create mode 100644 mail-worker/wrangler-action.toml diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml new file mode 100644 index 0000000..f949f9e --- /dev/null +++ b/.github/workflows/deploy-cloudflare.yml @@ -0,0 +1,117 @@ +name: 🚀 Deploy cloud-mail to Cloudflare Workers + +on: + push: + branches: [ main ] + paths: + - "mail-worker/**" + - "mail-vue/**" + workflow_dispatch: + +jobs: + Deploy-cloud-mail: + name: 🏗️ Build and Deploy + runs-on: ubuntu-latest + + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + D1_DATABASE_ID: ${{ secrets.D1_DATABASE_ID }} + KV_NAMESPACE_ID: ${{ secrets.KV_NAMESPACE_ID }} + R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }} + DOMAIN: ${{ secrets.DOMAIN }} + ADMIN: ${{ secrets.ADMIN }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + INIT_URL: ${{ secrets.INIT_URL }} + + steps: + - name: ➡️ Checkout repository + uses: actions/checkout@v4 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: "./mail-worker/package-lock.json" + + - name: 📥 Install dependencies + run: npm ci + working-directory: ./mail-worker + + - name: 📡 Disable wrangler telemetry + working-directory: ./mail-worker + # 使用 -c 指定配置文件 + run: npx wrangler telemetry disable -c wrangler-action.toml + + - name: 🤫 Set Worker secrets (ignore if already exists) + working-directory: ./mail-worker + run: | + WORKER_NAME="cloud-mail" + CONFIG_FILE="wrangler-action.toml" + echo "🔒 Attempting to create/update secrets using '$CONFIG_FILE'." + + for VAR in DOMAIN ADMIN JWT_SECRET; do + if [ -n "${!VAR}" ]; then + VAR_LOWER=$(echo "$VAR" | tr '[:upper:]' '[:lower:]') + echo ">> Processing secret: '$VAR_LOWER'" + (echo "${!VAR}" | npx wrangler secret put "$VAR_LOWER" --name "$WORKER_NAME" -c "$CONFIG_FILE") || true + else + echo "⚠️ Warning: GitHub Secret '$VAR' is not set. Skipping." + fi + done + + echo "✨ Secret processing complete." + + - name: 🛠️ Prepare Config and Deploy + working-directory: ./mail-worker + run: | + CONFIG_FILE="wrangler-action.toml" + echo "⚙️ Dynamically updating '$CONFIG_FILE' with binding IDs..." + + # 确保 sed 命令作用于 wrangler-action.toml 文件 + sed -i "s|\${D1_DATABASE_ID}|${D1_DATABASE_ID}|g" "$CONFIG_FILE" + sed -i "s|\${KV_NAMESPACE_ID}|${KV_NAMESPACE_ID}|g" "$CONFIG_FILE" + sed -i "s|\${R2_BUCKET_NAME}|${R2_BUCKET_NAME}|g" "$CONFIG_FILE" + + echo "🚀 Configuration updated. Starting deployment..." + npx wrangler deploy -c "$CONFIG_FILE" | grep -v "https://.*\.workers\.dev" || true + echo "✅ Deployment command executed." + + - name: 🗄️ Initialize Database (if INIT_URL is set) + run: | + if [ -z "$INIT_URL" ]; then + echo "✅ Deployment successful. INIT_URL not set, skipping initialization." + exit 0 + fi + + echo "⏳ Waiting 10 秒之前 before checking initialization status..." + sleep 10 + + HTTP_CODE=$(curl -s -w "%{http_code}" -o response.txt "$INIT_URL") + RESPONSE_BODY=$(cat response.txt) + + echo "🔎 Checking response... (Status: $HTTP_CODE)" + + if [ "$HTTP_CODE" = "200" ] && [ "$RESPONSE_BODY" = "初始化成功" ]; then + echo "🎉✅ Fresh initialization successful!" + elif [ "$HTTP_CODE" = "200" ]; then + echo "✅ Database is already initialized or in a stable state. Response: $RESPONSE_BODY" + else + echo "⚠️ Database initialization check failed with HTTP status: $HTTP_CODE. Please check your worker logs." + fi + + - name: 📣 Notify Final Status + if: always() + run: | + if [ "${{ job.status }}" == "success" ]; then + echo "🎉🎉🎉 Hooray! Deployment completed successfully! 🎉🎉🎉" + else + echo "❌❌❌ Oh no! The deployment failed. Please check the logs above for errors. ❌❌❌" + fi + + - name: Delete workflow runs + uses: GitRML/delete-workflow-runs@main + with: + retain_days: '3' + keep_minimum_runs: '0' \ No newline at end of file diff --git a/README.md b/README.md index 3cc962c..ad92521 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

@@ -22,11 +22,11 @@ [**👉 小白保姆教程-界面部署**](https://doc.skymail.ink) -| ![](demo/demo1.png) | ![](demo/demo2.png) | -|---------------------|---------------------| -| ![](demo/demo3.png) | ![](demo/demo4.png) | -| ![](demo/demo5.png) | ![](demo/demo6.png) | -| ![](demo/demo7.png) | ![](demo/demo8.png) | +| ![](/doc/demo/demo1.png) | ![](/doc/demo/demo2.png) | +|--------------------------|---------------------| +| ![](/doc/demo/demo3.png) | ![](/doc/demo/demo4.png) | +| ![](/doc/demo/demo5.png) | ![](/doc/demo/demo6.png) | +| ![](/doc/demo/demo7.png) | ![](/doc/demo/demo8.png) | @@ -153,16 +153,8 @@ jwt_secret = "" #登录身份令牌的密钥,随便填一串字符串 6. 部署完成登录网站,使用管理员账号可以在设置页面添加配置 R2域名 Turnstile密钥 等 -**邮件发送** - -1. 在 resend 官网注册后,点击左侧 Domains 添加并验证你的域名,等待验证完成 -2. 点击左侧 Api Keys 创建立api key, 复制token回到项目网站设置页面添加 resend token - -3. 点击左侧 Webhooks 添加回调地址 https://你的项目域名/api/webhooks - - 勾选✅ (email.bounced email.complained email.delivered email.delivery_delayed) - +[👉 使用 Github Action 部署](/doc/github-action.md) **本地运行** @@ -175,6 +167,14 @@ jwt_secret = "" #登录身份令牌的密钥,随便填一串字符串 3. 本地运行项目设置页面r2域名可设置为 http://127.0.0.1:8787/api/file +**邮件发送** + +1. 在 resend 官网注册后,点击左侧 Domains 添加并验证你的域名,等待验证完成 +2. 点击左侧 Api Keys 创建立api key, 复制token回到项目网站设置页面添加 resend token + +3. 点击左侧 Webhooks 添加回调地址 https://你的项目域名/api/webhooks + + 勾选✅ (email.bounced email.complained email.delivered email.delivery_delayed) ## 目录结构 diff --git a/demo/demo1.png b/doc/demo/demo1.png similarity index 100% rename from demo/demo1.png rename to doc/demo/demo1.png diff --git a/demo/demo2.png b/doc/demo/demo2.png similarity index 100% rename from demo/demo2.png rename to doc/demo/demo2.png diff --git a/demo/demo3.png b/doc/demo/demo3.png similarity index 100% rename from demo/demo3.png rename to doc/demo/demo3.png diff --git a/demo/demo4.png b/doc/demo/demo4.png similarity index 100% rename from demo/demo4.png rename to doc/demo/demo4.png diff --git a/demo/demo5.png b/doc/demo/demo5.png similarity index 100% rename from demo/demo5.png rename to doc/demo/demo5.png diff --git a/demo/demo6.png b/doc/demo/demo6.png similarity index 100% rename from demo/demo6.png rename to doc/demo/demo6.png diff --git a/demo/demo7.png b/doc/demo/demo7.png similarity index 100% rename from demo/demo7.png rename to doc/demo/demo7.png diff --git a/demo/demo8.png b/doc/demo/demo8.png similarity index 100% rename from demo/demo8.png rename to doc/demo/demo8.png diff --git a/demo/logo.png b/doc/demo/logo.png similarity index 100% rename from demo/logo.png rename to doc/demo/logo.png diff --git a/doc/github-action.md b/doc/github-action.md new file mode 100644 index 0000000..8f71329 --- /dev/null +++ b/doc/github-action.md @@ -0,0 +1,38 @@ +## Github Action 部署 + +**配置 Github 仓库** + +1. Fork 或克隆仓库 [https://github.com/eoao/cloud-mail](https://github.com/eoao/cloud-mail) +2. 进入您的 GitHub 仓库设置 +3. 转到 Settings → Secrets and variables → Actions → New Repository secrets +4. 添加以下 Secrets: + +| Secret 名称 | 必需 | 用途 | +| ----------------------- | :--: | ----------------------------------------------------- | +| `CLOUDFLARE_API_TOKEN` | ✅ | Cloudflare API 令牌(需要 Workers 和相关资源权限) | +| `CLOUDFLARE_ACCOUNT_ID` | ✅ | Cloudflare 账户 ID | +| `D1_DATABASE_ID` | ✅ | 您的 D1 数据库的 ID | +| `KV_NAMESPACE_ID` | ✅ | 您的 KV 命名空间的 ID | +| `R2_BUCKET_NAME` | ✅ | 您的 R2 存储桶的名称 | +| `DOMAIN` | ✅ | 您要用于邮件服务的域名(例如 `["xx.xx"],多域名用,分隔`) | +| `ADMIN` | ✅ | 您的管理员邮箱地址(例如 `admin@example.com`) | +| `JWT_SECRET` | ✅ | 用于生成和验证 JWT 的随机长字符串 | +| `INIT_URL` | ❌ | (可选)部署后用于初始化数据库的 Worker URL | + +--- + +**获取 Cloudflare API 令牌** + +1. 访问 [Cloudflare Dashboard](https://dash.cloudflare.com/profile/api-tokens) +2. 创建新的 API 令牌 +3. 选择"编辑 Cloudflare Workers"模板,并参照下表添加相应权限 + ![dc2e1dc8dcd217644759c46c6c705de1](https://i.miji.bid/2025/07/07/dc2e1dc8dcd217644759c46c6c705de1.png) +4. 保存令牌并复制到 GitHub Secrets 中的 `CLOUDFLARE_API_TOKEN` + +**获取 Cloudflare 账户 ID** +1. 账户 ID 可以在 Cloudflare 仪表盘的账户设置中找到。 +2. 复制到 GitHub Secrets 中的 `CLOUDFLARE_ACCOUNT_ID` + +**运行工作流** +1. 然后在Action页面手手动运行工作流,后续同步上游后会自动部署到 Cloudflare Workers。如未配置 `INIT_URL`,则需要手动访问 `https://你的项目域名/api/init/你的jwt_secret` 进行数据库初始化。 +2. 自动同步上游可使用bot或者手动点击Sync Upstream按钮。 \ No newline at end of file diff --git a/mail-worker/wrangler-action.toml b/mail-worker/wrangler-action.toml new file mode 100644 index 0000000..31bf19d --- /dev/null +++ b/mail-worker/wrangler-action.toml @@ -0,0 +1,36 @@ +name = "cloud-mail" +main = "src/index.js" +compatibility_date = "2025-04-09" +keep_vars = true + +[observability] +enabled = true + +[[d1_databases]] +binding = "db" +database_name = "cloud-mail" # 数据库的名称 +database_id = "${D1_DATABASE_ID}" # 使用占位符引用环境变量 + +[[kv_namespaces]] +binding = "kv" +id = "${KV_NAMESPACE_ID}" # 使用占位符引用环境变量 + +[[r2_buckets]] +binding = "r2" +bucket_name = "${R2_BUCKET_NAME}" # 使用占位符引用环境变量 + +[assets] +binding = "assets" #静态资源绑定名默认不可修改 +directory = "./dist" #前端vue项目打包的静态资源存放位置,默认dist +not_found_handling = "single-page-application" +run_worker_first = true + +[triggers] +crons = ["0 16 * * *"] #定时任务每天晚上12点执行 + + +#[vars] +#orm_log = false +#domain = [] #邮件域名可可配置多个 示例: ["example1.com","example2.com"] +#admin = "" #管理员的邮箱 示例: admin@example.com +#jwt_secret = "" #jwt令牌的密钥,随便填一串字符串 \ No newline at end of file