From 23c78b34083574b9c649c63fb0a29088533c981d Mon Sep 17 00:00:00 2001
From: shuaiplus <2327005759@qq.com>
Date: Sat, 7 Mar 2026 02:33:29 +0800
Subject: [PATCH] feat: update workflows and README for KV and R2 mode
switching
---
.github/workflows/switch-to-kv-mode.yml | 80 +++++++++++++++++++++++++
.github/workflows/switch-to-r2-mode.yml | 80 +++++++++++++++++++++++++
.github/workflows/sync-upstream.yml | 52 +++++++---------
README.md | 49 +++++++++------
README_EN.md | 40 +++++++++----
wrangler.kv.toml | 2 +-
6 files changed, 242 insertions(+), 61 deletions(-)
create mode 100644 .github/workflows/switch-to-kv-mode.yml
create mode 100644 .github/workflows/switch-to-r2-mode.yml
diff --git a/.github/workflows/switch-to-kv-mode.yml b/.github/workflows/switch-to-kv-mode.yml
new file mode 100644
index 0000000..b2b1eed
--- /dev/null
+++ b/.github/workflows/switch-to-kv-mode.yml
@@ -0,0 +1,80 @@
+name: Switch to KV mode
+
+on:
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ patch:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Configure git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+ - name: Switch wrangler.toml to KV mode
+ run: |
+ python - <<'PY'
+ import re
+ from pathlib import Path
+
+ path = Path("wrangler.toml")
+ text = path.read_text(encoding="utf-8")
+
+ pattern = (
+ r"\[\[r2_buckets\]\]\s*"
+ r'binding\s*=\s*"ATTACHMENTS"\s*'
+ r'bucket_name\s*=\s*"nodewarden-attachments"\s*'
+ )
+ replacement = (
+ '[[kv_namespaces]]\n'
+ 'binding = "ATTACHMENTS_KV"\n'
+ 'id = "placeholder"\n'
+ )
+
+ new_text, count = re.subn(pattern, replacement, text, count=1)
+ if count == 0 and 'binding = "ATTACHMENTS_KV"' not in text:
+ raise SystemExit("Expected R2 block not found in wrangler.toml")
+
+ if count > 0:
+ path.write_text(new_text, encoding="utf-8")
+ PY
+
+ - name: Update deploy button link in README files
+ env:
+ REPO_SLUG: ${{ github.repository }}
+ run: |
+ python - <<'PY'
+ import os
+ import re
+ from pathlib import Path
+
+ repo_slug = os.environ["REPO_SLUG"]
+ deploy_url = f"https://deploy.workers.cloudflare.com/?url=https://github.com/{repo_slug}"
+
+ pattern = re.compile(
+ r'https://deploy\.workers\.cloudflare\.com/\?url=https://github\.com/[^)\s]+'
+ )
+
+ for file_name in ("README.md", "README_EN.md"):
+ path = Path(file_name)
+ text = path.read_text(encoding="utf-8")
+ new_text, count = pattern.subn(deploy_url, text, count=2)
+ if count > 0:
+ path.write_text(new_text, encoding="utf-8")
+ PY
+
+ - name: Commit KV mode change
+ run: |
+ git add wrangler.toml README.md README_EN.md
+ git diff --cached --quiet && exit 0
+ git commit -m "chore: switch wrangler.toml to KV mode"
+ git push origin main
diff --git a/.github/workflows/switch-to-r2-mode.yml b/.github/workflows/switch-to-r2-mode.yml
new file mode 100644
index 0000000..3e7eb0e
--- /dev/null
+++ b/.github/workflows/switch-to-r2-mode.yml
@@ -0,0 +1,80 @@
+name: Switch to R2 mode
+
+on:
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ patch:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Configure git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+ - name: Switch wrangler.toml to R2 mode
+ run: |
+ python - <<'PY'
+ import re
+ from pathlib import Path
+
+ path = Path("wrangler.toml")
+ text = path.read_text(encoding="utf-8")
+
+ pattern = (
+ r"\[\[kv_namespaces\]\]\s*"
+ r'binding\s*=\s*"ATTACHMENTS_KV"\s*'
+ r'id\s*=\s*"placeholder"\s*'
+ )
+ replacement = (
+ '[[r2_buckets]]\n'
+ 'binding = "ATTACHMENTS"\n'
+ 'bucket_name = "nodewarden-attachments"\n'
+ )
+
+ new_text, count = re.subn(pattern, replacement, text, count=1)
+ if count == 0 and 'binding = "ATTACHMENTS"' not in text:
+ raise SystemExit("Expected KV block not found in wrangler.toml")
+
+ if count > 0:
+ path.write_text(new_text, encoding="utf-8")
+ PY
+
+ - name: Update deploy button link in README files
+ env:
+ REPO_SLUG: ${{ github.repository }}
+ run: |
+ python - <<'PY'
+ import os
+ import re
+ from pathlib import Path
+
+ repo_slug = os.environ["REPO_SLUG"]
+ deploy_url = f"https://deploy.workers.cloudflare.com/?url=https://github.com/{repo_slug}"
+
+ pattern = re.compile(
+ r'https://deploy\.workers\.cloudflare\.com/\?url=https://github\.com/[^)\s]+'
+ )
+
+ for file_name in ("README.md", "README_EN.md"):
+ path = Path(file_name)
+ text = path.read_text(encoding="utf-8")
+ new_text, count = pattern.subn(deploy_url, text, count=2)
+ if count > 0:
+ path.write_text(new_text, encoding="utf-8")
+ PY
+
+ - name: Commit R2 mode change
+ run: |
+ git add wrangler.toml README.md README_EN.md
+ git diff --cached --quiet && exit 0
+ git commit -m "chore: switch wrangler.toml to R2 mode"
+ git push origin main
diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml
index 5c352bc..316dbac 100644
--- a/.github/workflows/sync-upstream.yml
+++ b/.github/workflows/sync-upstream.yml
@@ -4,9 +4,6 @@ on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
- push:
- branches:
- - main
permissions:
contents: write
@@ -25,45 +22,40 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- - name: Sync main from upstream
+ - name: Sync main from upstream but keep local wrangler.toml
run: |
+ cp wrangler.toml /tmp/nodewarden-wrangler.toml
git remote add upstream https://github.com/shuaiplus/NodeWarden.git || true
git fetch upstream
-
git checkout main
git reset --hard upstream/main
- git push origin main --force
+ cp /tmp/nodewarden-wrangler.toml wrangler.toml
- - name: Rebuild kv from main
+ - name: Update deploy button links in README files
+ env:
+ REPO_SLUG: ${{ github.repository }}
run: |
- git fetch origin main kv || true
- git checkout -B kv origin/main
-
python - <<'PY'
+ import os
import re
from pathlib import Path
- path = Path("wrangler.toml")
- text = path.read_text(encoding="utf-8")
-
- pattern = (
- r"\[\[r2_buckets\]\]\s*"
- r'binding\s*=\s*"ATTACHMENTS"\s*'
- r'bucket_name\s*=\s*"nodewarden-attachments"\s*'
- )
- replacement = (
- '[[kv_namespaces]]\n'
- 'binding = "ATTACHMENTS_KV"\n'
- 'id = "REPLACE_WITH_KV_NAMESPACE_ID"\n'
+ repo_slug = os.environ["REPO_SLUG"]
+ deploy_url = f"https://deploy.workers.cloudflare.com/?url=https://github.com/{repo_slug}"
+ pattern = re.compile(
+ r'https://deploy\.workers\.cloudflare\.com/\?url=https://github\.com/[^)\s]+'
)
- new_text, count = re.subn(pattern, replacement, text, count=1)
- if count == 0:
- raise SystemExit("Expected R2 block not found in wrangler.toml")
-
- path.write_text(new_text, encoding="utf-8")
+ for file_name in ("README.md", "README_EN.md"):
+ path = Path(file_name)
+ text = path.read_text(encoding="utf-8")
+ new_text, _ = pattern.subn(deploy_url, text)
+ path.write_text(new_text, encoding="utf-8")
PY
- git add wrangler.toml
- git commit -m "chore(kv): sync from main" || echo "No changes"
- git push origin kv --force
+ - name: Push synced main
+ run: |
+ git add wrangler.toml README.md README_EN.md
+ git diff --cached --quiet && exit 0
+ git commit -m "chore: sync upstream while keeping local deploy config"
+ git push origin main --force
diff --git a/README.md b/README.md
index 0c080fd..2be93d9 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ English:[`README_EN.md`](./README_EN.md)
| 附件上传/下载 | ✅ | ✅ | Cloudflare R2 和 KV 二选一 |
| 导入导出功能 | ✅ | ✅ | 完整实现,含 Bitwarden 密码库+附件 ZIP 导入 |
| 网站图标代理 | ✅ | ✅ | 通过 `/icons/{hostname}/icon.png` |
-| passkey、TOTP字段 | ✅ | ✅ |完全支持,无需高级版 |
+| passkey、TOTP 字段 | ✅ | ✅ | 完全支持,无需高级版 |
| Send | ✅ | ✅ | Cloudflare R2 和 KV 二选一 |
| 多用户 | ✅ | ✅ | 完整的用户管理,邀请机制 |
| 组织/集合/成员权限 | ✅ | ❌ | 没必要实现 |
@@ -56,28 +56,42 @@ English:[`README_EN.md`](./README_EN.md)
**部署步骤:**
- > **若你只是想体验一下,直接点击第二步一键部署按钮即可**,以下操作为了长期更新使用
+> **若你只是想快速体验,直接点击第 2 步的一键部署按钮即可;以下步骤主要为了后续长期更新。**
-1. 首先Fork本仓库,命名为**NodeWarden**,取消选择 Copy the main branch only
-2. 根据对储存库的需求,**二选一**点击下面的按钮,修改项目名称为**NodeWarden2**,修改**JWT_SECRET**成32为随机字符串;
- - **R2**:需绑定银行卡;**单个附件/Send上限 100MB**(代码限制,可自行修改);**总量 10GB 免费**
-
+1. 先 Fork 本仓库,并命名为 **NodeWarden**。
+2. 点击下面的按钮,在打开的页面中将项目名称改为 **NodeWarden2**,并将 **JWT_SECRET** 设置为 32 位随机字符串。
+
[](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden)
- - **KV**:无需绑卡;**单个附件/Send 文件上限 25 MiB**(cloudflare限制,不可修改);**总量 1GB 免费**
+3. 部署完成后,在同一页面打开 Workers 设置,将 **Git 存储库** 断开连接。
+4. 在同一位置重新连接到第 1 步 Fork 的仓库,并在页面底部将 **名称** 改回 **NodeWarden**。
+5. GitHub 仓库 **NodeWarden2** 可以删除。
- [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden/tree/kv)
-3. 部署完成后,同一页面打开workers设置,将**Git存储库**断开连接
-4. 同一位置,**Git存储库**链接至第一步Fork的仓库(仓库名NodeWarden),**R2选择main分支,KV选择kv分支**(必须选对!!!)
-5. 仓库**NodeWarden2**可以删除
+
+📦 若未绑定银行卡、无法启用 R2 对象存储,可改用 KV 模式
+
+>- **R2**:需绑定银行卡;**单个附件 / Send 文件上限 100 MB**(代码限制,可自行修改);**总量 10 GB 免费**
+>- **KV**:无需绑卡;**单个附件 / Send 文件上限 25 MiB**(Cloudflare 限制,不可修改);**总量 1 GB 免费**
+>
+>1. 先 Fork 本仓库,并命名为 **NodeWarden**。
+>2. 打开 GitHub 仓库,进入 `Actions` 页面,运行 **Switch to KV mode**(自动将 [wrangler.toml](./wrangler.toml) 切换为 KV 配置。)
+>3. **在你自己的仓库中**点击下面的按钮,在打开的页面中将项目名称改为 **NodeWarden2**,并将 **JWT_SECRET** 设置为 32 位随机字符串。
+>
+> [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden)
+>
+>4. 部署完成后,在同一页面打开 Workers 设置,将 **Git 存储库** 断开连接。
+>5. 在同一位置重新连接到第 1 步 Fork 的仓库,并在页面底部将 **名称** 改回 **NodeWarden**。
+>6. GitHub 仓库 **NodeWarden2** 可以删除。
+
> [!TIP]
> 同步上游(更新仓库):
->- 手动:Github打开你Fork的私人仓库,看到顶部同步提示时,点击 “Sync fork”。
->- 自动:进入你的 Fork 仓库 → Actions,点击 “I understand my workflows, go ahead and enable them”,每天凌晨三点自动同步至上游
+>- 手动:打开你 Fork 的 GitHub 仓库,看到顶部同步提示后,点击 `Sync fork`。
+>- 自动:进入你的 Fork 仓库 -> `Actions`,点击 `I understand my workflows, go ahead and enable them`,启用后,`Sync upstream` 会在每天凌晨 3 点自动同步上游 `main`,并保留你当前的 [wrangler.toml](./wrangler.toml) 配置不被覆盖。
+
### CLI 部署
@@ -102,10 +116,10 @@ npm run deploy
# (可选)KV 模式(无 R2 / 无信用卡)
npx wrangler kv namespace create ATTACHMENTS_KV
-# 将返回的 namespace id 填入 wrangler.kv.toml 的 [[kv_namespaces]].id
+# 将返回的 namespace id 替换 wrangler.kv.toml 中 `id = "placeholder"` 里的 placeholder(保留双引号)
npm run deploy:kv
-# 需更新时重新拉取仓库,重新部署即可,无需创建云资源
+# 后续更新时重新拉取仓库并重新部署即可,无需重复创建云资源
git clone https://github.com/shuaiplus/NodeWarden.git
cd NodeWarden
npm run deploy
@@ -129,13 +143,13 @@ A: 在客户端中选择「导出密码库」,保存 JSON 文件。
**Q: 导入导出支持哪些格式?**
A: 支持 Bitwarden `json/csv/密码库+附件 zip` 和 NodeWarden `密码库+附件 json`(均含加密模式),且导入下拉中看到的格式都可直接导入。
-A: 另外支持直接导入 Bitwarden `密码库+附件 zip`,这条路径官方 Bitwarden Web 不支持。
+A: 另外还支持直接导入 Bitwarden `密码库+附件 zip`,这条路径官方 Bitwarden Web 暂不支持。
**Q: 忘记主密码怎么办?**
A: 无法恢复,这是端到端加密的特性。建议妥善保管主密码。
**Q: 可以多人使用吗?**
-A: 支持。第一个注册的用户自动成为管理员,管理员可在管理页面生成邀请码,其他用户凭邀请码注册。
+A: 支持。第一个注册的用户会自动成为管理员;管理员可在管理页面生成邀请码,其他用户凭邀请码注册。
---
@@ -150,6 +164,7 @@ LGPL-3.0 License
- [Bitwarden](https://bitwarden.com/) - 原始设计和客户端
- [Vaultwarden](https://github.com/dani-garcia/vaultwarden) - 服务器实现参考
- [Cloudflare Workers](https://workers.cloudflare.com/) - 无服务器平台
+
---
## Star History
diff --git a/README_EN.md b/README_EN.md
index cd3a19c..dc2826a 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -57,25 +57,39 @@
**Deploy steps:**
-- **If you just want to try it quickly, you can simply click one of the deploy buttons in step 2.**
+> **If you only want a quick trial, simply click the one-click deploy button in step 2. The remaining steps are mainly for long-term maintenance.**
-1. Fork this repository, name it **NodeWarden**, and make sure **Copy the main branch only** is **unchecked**.
-2. Choose one deployment mode below, rename the project to **NodeWarden2**, and set **JWT_SECRET** to a random 32-character string.
- - **R2**: requires a payment method; **single attachment/Send file limit is 100 MB** (project-level limit, editable in code); **10 GB free storage**.
+1. Fork this repository and name it **NodeWarden**.
+2. Click the button below. On the page that opens, rename the project to **NodeWarden2** and set **JWT_SECRET** to a random 32-character string.
- [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden)
-
- - **KV**: no card required; **single attachment/Send file limit is 25 MiB** (Cloudflare platform limit, not editable); **1 GB free storage**.
-
- [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden/tree/kv)
+ [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden)
3. After deployment, open the Worker settings on the same page and disconnect the **Git repository**.
-4. Reconnect the **Git repository** to the fork from step 1. This branch selection must match the button you used: **R2 uses `main`, KV uses `kv`**.
+4. Reconnect the **Git repository** to the fork from step 1, then change the **Name** field at the bottom to **NodeWarden**.
5. The temporary **NodeWarden2** repository can be deleted.
+
+📦 If you do not have a payment method attached and cannot enable R2 object storage, you can use KV mode instead
+
+
+
+>- **R2**: requires a payment method; **single attachment / Send file limit is 100 MB** (project-level limit, editable in code); **10 GB free storage**
+>- **KV**: no card required; **single attachment / Send file limit is 25 MiB** (Cloudflare limit, not editable); **1 GB free storage**
+>
+>1. Fork this repository and name it **NodeWarden**.
+>2. Open your GitHub repository, go to the `Actions` page, and run **Switch to KV mode** (this automatically switches [wrangler.toml](./wrangler.toml) to the KV configuration).
+>3. **In your own repository**, click the button below. On the page that opens, rename the project to **NodeWarden2** and set **JWT_SECRET** to a random 32-character string.
+>
+> [](https://deploy.workers.cloudflare.com/?url=https://github.com/shuaiplus/NodeWarden)
+>
+>4. After deployment, open the Worker settings on the same page and disconnect the **Git repository**.
+>5. Reconnect the **Git repository** to the fork from step 1, then change the **Name** field at the bottom to **NodeWarden**.
+>6. The GitHub repository **NodeWarden2** can be deleted.
+
+
> [!TIP]
> Sync upstream (keep your fork updated):
->- Manual: open your fork on GitHub and click **Sync fork** when prompted.
->- Automatic: in your fork, go to **Actions**, click **I understand my workflows, go ahead and enable them**. It will sync `main` from upstream, rebuild `kv` from `main`, and apply the KV `wrangler.toml` changes automatically every day at 3 AM.
+>- Manual: open your fork on GitHub and click `Sync fork` when prompted.
+>- Automatic: in your fork, go to `Actions`, click `I understand my workflows, go ahead and enable them`. Once enabled, `Sync upstream` will automatically sync the upstream `main` branch every day at 3 AM and keep your current [wrangler.toml](./wrangler.toml) configuration unchanged.
### CLI deploy
@@ -99,7 +113,7 @@ npm run deploy
# (Optional) KV mode (no R2 / no credit card)
npx wrangler kv namespace create ATTACHMENTS_KV
-# Put returned namespace id into wrangler.kv.toml -> [[kv_namespaces]].id
+# Replace placeholder inside `id = "placeholder"` in wrangler.kv.toml with the returned namespace id (keep the quotes)
npm run deploy:kv
# To update later: re-clone and re-deploy — no need to recreate cloud resources
diff --git a/wrangler.kv.toml b/wrangler.kv.toml
index d4121c0..5509bb4 100644
--- a/wrangler.kv.toml
+++ b/wrangler.kv.toml
@@ -12,4 +12,4 @@ database_name = "nodewarden-db"
[[kv_namespaces]]
binding = "ATTACHMENTS_KV"
-id = "REPLACE_WITH_KV_NAMESPACE_ID"
+id = "placeholder"