From 5837c0ec3eedff766a494fe4461daddb94cb3adb Mon Sep 17 00:00:00 2001 From: tianma-if Date: Tue, 21 Jul 2026 03:54:39 +0800 Subject: [PATCH] feat: add Cloudflare one-click deployment (#58) --- .dev.vars.example | 4 + .github/workflows/deploy-tools.yml | 8 +- .github/workflows/sync-edgeever-upstream.yml | 118 +++++++++++++++++++ README.md | 26 ++-- README.zh-CN.md | 26 ++-- apps/mobile/app.json | 4 +- docs/agent-deploy-cloudflare.md | 6 +- docs/cloudflare-workers-builds.md | 8 +- docs/cloudflare-workers-builds.zh-CN.md | 8 +- docs/deploy-cloudflare-button.md | 33 ++++++ docs/deploy-cloudflare-button.zh-CN.md | 33 ++++++ docs/manual-deploy.md | 106 ++++++++--------- docs/manual-deploy.zh-CN.md | 106 ++++++++--------- docs/promotion/promotion_article_juejin.md | 2 +- package.json | 11 +- scripts/cloudflare-workers-builds.mjs | 2 +- scripts/run-wrangler.mjs | 9 +- scripts/verify-deployment.mjs | 2 +- tests/deployment-entrypoints.test.ts | 45 +++++++ 19 files changed, 405 insertions(+), 152 deletions(-) create mode 100644 .dev.vars.example create mode 100644 .github/workflows/sync-edgeever-upstream.yml create mode 100644 docs/deploy-cloudflare-button.md create mode 100644 docs/deploy-cloudflare-button.zh-CN.md create mode 100644 tests/deployment-entrypoints.test.ts diff --git a/.dev.vars.example b/.dev.vars.example new file mode 100644 index 00000000..359ce28b --- /dev/null +++ b/.dev.vars.example @@ -0,0 +1,4 @@ +# Cloudflare Deploy to Cloudflare uses this file to request the required +# Worker Secret during one-click setup. Leave the value empty in source control; +# every installation must choose its own strong password. +EDGE_EVER_AUTH_PASSWORD= diff --git a/.github/workflows/deploy-tools.yml b/.github/workflows/deploy-tools.yml index 722d783b..26294ab3 100644 --- a/.github/workflows/deploy-tools.yml +++ b/.github/workflows/deploy-tools.yml @@ -7,6 +7,9 @@ on: - "scripts/run-wrangler.mjs" - "scripts/wrangler-runner.mjs" - "scripts/verify-deployment.mjs" + - ".dev.vars.example" + - ".github/workflows/sync-edgeever-upstream.yml" + - "tests/deployment-entrypoints.test.ts" - "tests/wrangler-runner.test.ts" - "tests/deployment-verify.test.ts" - "package.json" @@ -20,6 +23,9 @@ on: - "scripts/run-wrangler.mjs" - "scripts/wrangler-runner.mjs" - "scripts/verify-deployment.mjs" + - ".dev.vars.example" + - ".github/workflows/sync-edgeever-upstream.yml" + - "tests/deployment-entrypoints.test.ts" - "tests/wrangler-runner.test.ts" - "tests/deployment-verify.test.ts" - "package.json" @@ -55,4 +61,4 @@ jobs: run: bun scripts/run-wrangler.mjs --version - name: Test deployment tooling - run: bun test tests/wrangler-runner.test.ts tests/deployment-verify.test.ts + run: bun test tests/wrangler-runner.test.ts tests/deployment-verify.test.ts tests/deployment-entrypoints.test.ts diff --git a/.github/workflows/sync-edgeever-upstream.yml b/.github/workflows/sync-edgeever-upstream.yml new file mode 100644 index 00000000..8621f26d --- /dev/null +++ b/.github/workflows/sync-edgeever-upstream.yml @@ -0,0 +1,118 @@ +name: Update deployed EdgeEver + +on: + schedule: + - cron: "17 2 * * *" + workflow_dispatch: + inputs: + channel: + description: Update channel for this run + required: true + default: stable + type: choice + options: + - stable + - edge + +permissions: + contents: write + issues: write + +concurrency: + group: edgeever-upstream-update + cancel-in-progress: false + +jobs: + update: + if: github.repository != 'tianma-if/edgeever' + name: Sync, verify, and publish + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPSTREAM_REPOSITORY: tianma-if/edgeever + UPDATE_CHANNEL: ${{ inputs.channel || vars.EDGE_EVER_UPDATE_CHANNEL || 'stable' }} + + steps: + - name: Checkout deployed repository + uses: actions/checkout@v5 + with: + ref: main + fetch-depth: 0 + + - name: Resolve upstream version + id: upstream + shell: bash + run: | + set -euo pipefail + git remote add upstream "https://github.com/${UPSTREAM_REPOSITORY}.git" + git fetch upstream main --tags + + case "${UPDATE_CHANNEL}" in + stable) + target_ref="$(gh api "repos/${UPSTREAM_REPOSITORY}/releases/latest" --jq '.tag_name')" + ;; + edge) + target_ref="upstream/main" + ;; + *) + echo "Unsupported update channel: ${UPDATE_CHANNEL}" >&2 + exit 1 + ;; + esac + + target_commit="$(git rev-parse "${target_ref}^{commit}")" + if git merge-base --is-ancestor "${target_commit}" HEAD; then + update_required=false + else + update_required=true + fi + + { + echo "target_ref=${target_ref}" + echo "target_commit=${target_commit}" + echo "update_required=${update_required}" + } >> "${GITHUB_OUTPUT}" + + - name: Merge upstream update + if: steps.upstream.outputs.update_required == 'true' + env: + TARGET_COMMIT: ${{ steps.upstream.outputs.target_commit }} + TARGET_REF: ${{ steps.upstream.outputs.target_ref }} + run: | + git config user.name "edgeever-updater[bot]" + git config user.email "edgeever-updater[bot]@users.noreply.github.com" + git merge --no-edit -m "chore: update EdgeEver from ${TARGET_REF}" "${TARGET_COMMIT}" + + - name: Set up Bun + if: steps.upstream.outputs.update_required == 'true' + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Verify update + if: steps.upstream.outputs.update_required == 'true' + run: | + bun install --frozen-lockfile + bun run db:migrate:local + bun test + bun run typecheck + bun run build + + - name: Publish update + if: steps.upstream.outputs.update_required == 'true' + run: git push origin HEAD:main + + - name: Report an update failure + if: failure() + continue-on-error: true + env: + TARGET_REF: ${{ steps.upstream.outputs.target_ref || 'unknown' }} + run: | + title="EdgeEver automatic update requires attention" + existing="$(gh issue list --state open --search "${title} in:title" --json number --jq '.[0].number // empty')" + if [ -z "${existing}" ]; then + gh issue create \ + --title "${title}" \ + --body "The daily update from \`${UPSTREAM_REPOSITORY}\` (${TARGET_REF}) could not be merged or verified. The current production deployment was left unchanged. Open the failed **Update deployed EdgeEver** workflow run for details, then retry it manually after resolving the problem." + fi diff --git a/README.md b/README.md index 77e6bcd1..b78ee5fb 100644 --- a/README.md +++ b/README.md @@ -50,25 +50,33 @@ The public demo resets every Monday at 1:00 AM (China Standard Time) and restore ## Deployment +### Deploy to Cloudflare (Recommended) + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/tianma-if/edgeever) + +Cloudflare creates a GitHub repository for the installation, provisions the Worker, D1 database, and R2 bucket, requests the initial login password as a Secret, applies migrations, and connects Workers Builds. The generated repository checks for upstream EdgeEver updates daily; the default `stable` channel follows formal Releases, while the optional `edge` channel follows upstream `main`. + +See [Deploy EdgeEver with Cloudflare](docs/deploy-cloudflare-button.md) for setup, verification, update channels, and troubleshooting. + ### Deploy with an AI Agent > 💡 **Note:** Under the hood, AI Agent deployment uses the exact same deterministic CLI commands (Wrangler and Bun scripts) as the manual flow. The AI agent simply executes these standard commands in the terminal on your behalf for convenience; it is not a non-deterministic, black-box deployment. -Copy this prompt into your AI coding assistant, such as Claude Code, Codex, OpenClaw, Antigravity, or Cursor. It covers the first installation and the automatic-update setup: +Copy this prompt into your AI coding assistant, such as Claude Code, Codex, OpenClaw, Antigravity, or Cursor. It uses the same deployment core as the button and manual flows: -**Recommendation:** Before deployment, configure GitHub and Cloudflare MCP servers, plugins, or other supported integrations for your AI Agent. This allows it to fork the repository, create the required Cloudflare resources, deploy the application, and connect the instance to Cloudflare Workers Builds. +**Recommendation:** Before deployment, configure GitHub and Cloudflare MCP servers, plugins, or other supported integrations for your AI Agent. ```text Please follow these steps: -1. Fork the EdgeEver upstream repository: https://github.com/tianma-if/edgeever -2. Use the forked repository to create the Cloudflare resources and complete EdgeEver's first deployment. -3. Run `bun run deploy:builds:setup` to connect the deployed Worker to the fork's `main` branch through Cloudflare Workers Builds. If setup needs a token, use a User API Token, not an Account API Token. -4. After that, GitHub Sync fork or any push to `main` must automatically build, apply D1 migrations, and deploy the instance. +1. Create a GitHub repository from https://github.com/tianma-if/edgeever and clone it locally. +2. Follow docs/agent-deploy-cloudflare.md to create Cloudflare resources and run `bun run deploy:manual`. +3. Run `bun run deploy:builds:setup` to connect the deployed Worker to the repository's `main` branch through Cloudflare Workers Builds. If setup needs a token, use a User API Token, not an Account API Token. +4. Verify the first automatic build and the daily upstream-update workflow. ``` Agents should follow [AI Agent Cloudflare Deployment](docs/agent-deploy-cloudflare.md). -After the first deployment, see [Cloudflare Workers Builds](docs/cloudflare-workers-builds.md) for automatic updates. The same deployment flow is used by official instances and forks. +After the first deployment, see [Cloudflare Workers Builds](docs/cloudflare-workers-builds.md). All installation entry points use the same build, migration, deployment, and verification pipeline. > Common pitfall: Cloudflare R2, D1, and Workers may still require a Visa card during activation or usage, even when you stay within the free quotas. @@ -76,9 +84,9 @@ After the first deployment, see [Cloudflare Workers Builds](docs/cloudflare-work ### Manual Deployment -Please refer to the [Cloudflare Manual Deployment Guide](docs/manual-deploy.md) for first-time manual installation, Cloudflare resource setup, and emergency recovery. After the first deployment, connect Workers Builds; future updates arrive through GitHub **Sync fork** or pushes to `main`. +Please refer to the [Cloudflare Manual Deployment Guide](docs/manual-deploy.md) for advanced first-time installation, Cloudflare resource setup, troubleshooting, and emergency recovery. After the first deployment, connect Workers Builds; future repository updates deploy automatically. -The automated helper commands are recommended. The template uses `admin` / `admin123` for the initial login, and the password can be changed later in Personal Settings. If you create the Cloudflare resources manually, finish configuring `.env.local`—including the D1 ID, R2 bucket, and the 400-day session limit—before running `bun run deploy`. +The automated helper commands are recommended. The manual template uses `admin` / `admin123` for the initial login, and the password can be changed later in Personal Settings. If you create the Cloudflare resources manually, finish configuring `.env.local`—including the D1 ID, R2 bucket, and the 400-day session limit—before running `bun run deploy:manual`. Production deployments fail closed: when D1 migrations or the login Secret are missing, the instance shows a diagnosable configuration error and denies access instead of falling back to an unauthenticated workspace. Never insert plaintext passwords into D1; use the recovery command documented in the manual deployment guide. diff --git a/README.zh-CN.md b/README.zh-CN.md index 61db8efa..71095b65 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -49,23 +49,31 @@ EdgeEver 是一个开源、自托管、Cloudflare-native 的现代笔记工作 ## 部署 +### Cloudflare 一键部署(推荐) + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/tianma-if/edgeever) + +Cloudflare 会为本次安装创建 GitHub 仓库,自动创建 Worker、D1 数据库和 R2 bucket,要求用户设置初始登录密码 Secret,执行 migration,并连接 Workers Builds。生成的仓库每天检查一次 EdgeEver 上游更新:默认 `stable` 通道跟随正式 Release,可选 `edge` 通道跟随上游 `main`。 + +配置、验证、更新通道和排错说明见[使用 Cloudflare 一键部署 EdgeEver](docs/deploy-cloudflare-button.zh-CN.md)。 + ### 通过AI Agent 一句话部署 > 💡 **提示:** AI Agent 部署的底层逻辑与标准的手动部署完全一致。AI 助手只是代为在终端执行标准的、确定性的配置与部署命令(如 `wrangler` 和 `bun` 脚本),并非使用非确定性的黑盒部署方式。 -将下方提示词复制给你的 AI 助手(Claude Code、Codex、OpenClaw、Antigravity、Cursor 等),它会完成首次安装并配置后续自动更新。 +将下方提示词复制给你的 AI 助手(Claude Code、Codex、OpenClaw、Antigravity、Cursor 等)。该入口与一键部署和手动部署共用同一套部署内核。 -**建议:** 开始部署前,请先为 AI Agent 配置 GitHub 和 Cloudflare 的 MCP、插件或其他可用集成,以便 Agent 完成仓库 Fork、Cloudflare 资源创建、应用部署和 Workers Builds 自动部署连接。 +**建议:** 开始部署前,请先为 AI Agent 配置 GitHub 和 Cloudflare 的 MCP、插件或其他可用集成。 ```text 请按以下流程操作: -1. Fork EdgeEver 上游仓库:https://github.com/tianma-if/edgeever -2. 使用 Fork 后的仓库创建 Cloudflare 资源并完成 EdgeEver 首次部署。 -3. 运行 `bun run deploy:builds:setup`,通过 Cloudflare Workers Builds 将已部署的 Worker 连接到 Fork 仓库的 `main` 分支;如需 token,使用 User API Token,不要使用 Account API Token。 -4. 后续 GitHub Sync fork 或任何推送到 `main` 的更新,都必须自动构建、执行 D1 migration 并发布该实例。 +1. 基于 https://github.com/tianma-if/edgeever 创建 GitHub 仓库并克隆到本地。 +2. 严格按照 docs/agent-deploy-cloudflare.md 创建 Cloudflare 资源并运行 `bun run deploy:manual`。 +3. 运行 `bun run deploy:builds:setup`,通过 Cloudflare Workers Builds 将已部署的 Worker 连接到仓库的 `main` 分支;如需 token,使用 User API Token,不要使用 Account API Token。 +4. 验证首次自动构建和每日上游更新工作流。 ``` -Agent 应优先按 [AI Agent Cloudflare Deployment](docs/agent-deploy-cloudflare.md) 执行。首次部署后,请参阅 [Cloudflare Workers Builds 自动部署](docs/cloudflare-workers-builds.zh-CN.md);官方实例和 Fork 使用同一套发布流程。 +Agent 应优先按 [AI Agent Cloudflare Deployment](docs/agent-deploy-cloudflare.md) 执行。首次部署后,请参阅 [Cloudflare Workers Builds 自动部署](docs/cloudflare-workers-builds.zh-CN.md)。所有安装入口共用同一套构建、migration、发布和验证流水线。 > 常见踩坑:Cloudflare 的 R2、D1 和 Worker 即使使用免费额度,在开通或使用过程中也可能要求绑定一张 Visa 卡。国内用户可以考虑办理招商银行多币种卡,拿到 Visa 卡后绑定到 Cloudflare 账号即可。 @@ -73,9 +81,9 @@ Agent 应优先按 [AI Agent Cloudflare Deployment](docs/agent-deploy-cloudflare ### 手动部署 -关于首次手动安装、Cloudflare 资源配置与紧急修复,请参考 [Cloudflare 手动部署指南](docs/manual-deploy.zh-CN.md)。首次部署后,请配置 Cloudflare Workers Builds;之后通过 GitHub **Sync fork** 或推送 `main` 自动更新。 +关于高级首次安装、Cloudflare 资源配置、故障排查与紧急修复,请参考 [Cloudflare 手动部署指南](docs/manual-deploy.zh-CN.md)。首次部署后,请配置 Cloudflare Workers Builds;之后仓库更新会自动部署。 -推荐优先使用自动化辅助命令。配置模板使用 `admin` / `admin123` 作为初始登录账号密码,之后可在个人设置中修改密码。如果选择完全手动创建 Cloudflare 资源,必须先在 `.env.local` 中完成 D1 ID、R2 bucket 和 400 天会话期限等配置,再执行 `bun run deploy`。 +推荐优先使用自动化辅助命令。手动配置模板使用 `admin` / `admin123` 作为初始登录账号密码,之后可在个人设置中修改密码。如果选择完全手动创建 Cloudflare 资源,必须先在 `.env.local` 中完成 D1 ID、R2 bucket 和 400 天会话期限等配置,再执行 `bun run deploy:manual`。 生产部署采用安全关闭策略:D1 未迁移或登录 Secret 缺失时,实例会显示可诊断的配置错误并拒绝访问,不会自动退化成免登录模式。请勿直接向 D1 写入明文密码;故障恢复命令见手动部署指南。 diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 8310bfa2..79cef9d7 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -3,7 +3,7 @@ "name": "EdgeEver", "slug": "edgeever", "scheme": "edgeever", - "version": "1.0.0", + "version": "1.1.0", "runtimeVersion": { "policy": "appVersion" }, @@ -28,7 +28,7 @@ }, "android": { "package": "org.edgeever.mobile", - "versionCode": 25, + "versionCode": 26, "adaptiveIcon": { "backgroundColor": "#d1fae5", "backgroundImage": "./assets/icon.png", diff --git a/docs/agent-deploy-cloudflare.md b/docs/agent-deploy-cloudflare.md index a4aff185..258b55e3 100644 --- a/docs/agent-deploy-cloudflare.md +++ b/docs/agent-deploy-cloudflare.md @@ -15,7 +15,7 @@ Run in order: ```sh bun run deploy:setup bun run deploy:doctor -bun run deploy +bun run deploy:manual ``` If the user supplied a custom initial password, replace the first command with: @@ -24,7 +24,7 @@ If the user supplied a custom initial password, replace the first command with: EDGE_EVER_PASSWORD='' bun run deploy:setup ``` -Do not deploy until `deploy:doctor` passes. After deployment, verify that `/api/health` returns `200` with `"ok": true`, then verify `/api/openapi.json` and login. A `database_not_ready` or `auth_not_configured` response means deployment is incomplete and must not be handed off as successful. The default initial login is `admin` / `admin123`. +Do not deploy until `deploy:doctor` passes. `deploy:manual` uses the same build, migration, Worker deployment, and verification core as Cloudflare one-click deployment and Workers Builds. After deployment, verify that `/api/health` returns `200` with `"ok": true`, then verify `/api/openapi.json` and login. A `database_not_ready` or `auth_not_configured` response means deployment is incomplete and must not be handed off as successful. The default initial login is `admin` / `admin123`. ## Automatic Updates @@ -34,4 +34,4 @@ After the first deployment, run: bun run deploy:builds:setup ``` -Follow [Cloudflare Workers Builds](cloudflare-workers-builds.md) only when the command requests authorization or an API token. Deployment is complete when the verification build succeeds and future pushes to `main` automatically deploy the instance. +Follow [Cloudflare Workers Builds](cloudflare-workers-builds.md) only when the command requests authorization or an API token. Deployment is complete when the verification build succeeds and future pushes to `main` automatically deploy the instance. Confirm that the repository's **Update deployed EdgeEver** workflow is available and enabled; GitHub disables scheduled workflows by default on public forks. Its default `stable` channel checks formal upstream Releases daily. diff --git a/docs/cloudflare-workers-builds.md b/docs/cloudflare-workers-builds.md index 83148ec6..486cb5a6 100644 --- a/docs/cloudflare-workers-builds.md +++ b/docs/cloudflare-workers-builds.md @@ -1,10 +1,10 @@ # Cloudflare Workers Builds -Cloudflare Workers Builds deploys EdgeEver whenever `main` changes, including after GitHub **Sync fork**. Use local `bun run deploy` only for the first installation or emergency recovery. +Cloudflare Workers Builds deploys EdgeEver whenever `main` changes. Cloudflare one-click deployment connects it automatically; CLI installations use local `bun run deploy:manual` only for first installation or emergency recovery. ## Setup -Complete the [first deployment](manual-deploy.md), then run: +Cloudflare one-click installations do not need this setup command. For CLI or Agent installations, complete the [first deployment](manual-deploy.md), then run: ```sh bun run deploy:builds:setup @@ -16,7 +16,7 @@ Only complete the following steps when the command asks for them. ### GitHub authorization -Approve installation of the **Cloudflare Workers & Pages** GitHub App for the fork. This is the app's name; an EdgeEver instance does not require a Pages project. The command handles the repository connection after authorization. +Approve installation of the **Cloudflare Workers & Pages** GitHub App for the deployment repository. This is the app's name; an EdgeEver instance does not require a Pages project. The command handles the repository connection after authorization. ### Configuration API token @@ -41,6 +41,6 @@ If the command reports that no deployment API token is available, open **Worker* ## Updates and Troubleshooting -After setup, push to `main` or use GitHub **Sync fork**. Cloudflare installs dependencies, checks and builds the app, applies new D1 migrations, and deploys the Worker. No GitHub Actions secrets or local redeployment are required. +After setup, every push to `main` makes Cloudflare install dependencies, check and build the app, apply new D1 migrations, deploy the Worker, and verify the result. The **Update deployed EdgeEver** GitHub workflow supplies upstream updates daily: `stable` follows formal Releases by default, while the repository variable `EDGE_EVER_UPDATE_CHANNEL=edge` follows upstream `main`. No Cloudflare deployment secrets or local redeployment are required in GitHub Actions. If a build fails, inspect its log in the Worker **Deployments** tab. Rerun `bun run deploy:builds:setup` when instance settings change. diff --git a/docs/cloudflare-workers-builds.zh-CN.md b/docs/cloudflare-workers-builds.zh-CN.md index a3644cd9..6f888b25 100644 --- a/docs/cloudflare-workers-builds.zh-CN.md +++ b/docs/cloudflare-workers-builds.zh-CN.md @@ -1,10 +1,10 @@ # Cloudflare Workers Builds 自动部署 -Cloudflare Workers Builds 会在 `main` 发生变化时自动部署 EdgeEver,包括 GitHub **Sync fork** 带来的更新。本地 `bun run deploy` 只用于首次安装和紧急修复。 +Cloudflare Workers Builds 会在 `main` 发生变化时自动部署 EdgeEver。Cloudflare 一键部署会自动完成连接;CLI 安装只在首次部署或紧急修复时使用本地 `bun run deploy:manual`。 ## 配置 -先完成[首次部署](manual-deploy.zh-CN.md),然后执行: +Cloudflare 一键部署实例不需要执行该配置命令。CLI 或 Agent 安装请先完成[首次部署](manual-deploy.zh-CN.md),然后执行: ```sh bun run deploy:builds:setup @@ -16,7 +16,7 @@ bun run deploy:builds:setup ### GitHub 授权 -为 Fork 安装并授权 **Cloudflare Workers & Pages** GitHub App。这只是应用名称,EdgeEver 实例不需要部署 Pages 项目。授权完成后,命令会继续配置仓库连接。 +为部署仓库安装并授权 **Cloudflare Workers & Pages** GitHub App。这只是应用名称,EdgeEver 实例不需要部署 Pages 项目。授权完成后,命令会继续配置仓库连接。 ### 配置 API token @@ -41,6 +41,6 @@ EDGE_EVER_BUILDS_API_TOKEN= ## 更新与排错 -配置完成后,只需推送到 `main` 或使用 GitHub **Sync fork**。Cloudflare 会安装依赖、检查并构建应用、执行新的 D1 migration,然后部署 Worker;不需要 GitHub Actions Secrets 或本地重新部署。 +配置完成后,任何推送到 `main` 的提交都会让 Cloudflare 安装依赖、检查并构建应用、执行新的 D1 migration、部署 Worker 并验证结果。GitHub 中的 **Update deployed EdgeEver** 工作流每天提供上游更新:默认 `stable` 跟随正式 Release,设置 Repository Variable `EDGE_EVER_UPDATE_CHANNEL=edge` 后跟随上游 `main`。GitHub Actions 不需要保存 Cloudflare 部署 Secret,也不需要本地重新部署。 构建失败时,在 Worker 的 **Deployments** 页面查看日志。实例配置变化后,重新执行 `bun run deploy:builds:setup`。 diff --git a/docs/deploy-cloudflare-button.md b/docs/deploy-cloudflare-button.md new file mode 100644 index 00000000..8572a408 --- /dev/null +++ b/docs/deploy-cloudflare-button.md @@ -0,0 +1,33 @@ +# Deploy EdgeEver with Cloudflare + +The **Deploy to Cloudflare** button is the recommended first-installation path. It creates a repository in your GitHub account, provisions the Worker, D1 database, and R2 bucket, applies the database migrations, and connects the repository to Cloudflare Workers Builds. + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/tianma-if/edgeever) + +## First Installation + +1. Sign in to Cloudflare and GitHub, then open the button above. +2. Authorize the **Cloudflare Workers & Pages** GitHub App when requested. +3. Choose the destination repository, Worker name, D1 database name, and R2 bucket name. +4. Set `EDGE_EVER_AUTH_PASSWORD` to a strong password that is unique to this instance. The field is a Worker Secret and must not be committed to Git. +5. Save and deploy. Cloudflare runs the repository's common deployment pipeline: build, remote D1 migrations, Worker deployment, and deployment verification. +6. Open the resulting `*.workers.dev` URL. Confirm `/api/health` returns `200` with `"ok": true`, then sign in as `admin` with the password chosen during setup. + +EdgeEver fails closed. If the D1 migrations or authentication Secret are missing, the instance returns `database_not_ready` or `auth_not_configured` instead of exposing an unauthenticated workspace. + +## Automatic Updates + +The generated repository is connected to Workers Builds, so every push to `main` builds, migrates, verifies, and deploys the instance. It also contains the **Update deployed EdgeEver** workflow, which checks the upstream repository once per day. + +The default `stable` channel follows the latest formal GitHub Release. Before publishing an update, the workflow merges it locally and verifies dependency installation, local D1 migrations, automated tests, type checking, and the production web build. A failed merge or check leaves the deployed `main` branch unchanged and attempts to create an Issue with recovery guidance. + +To follow the latest upstream `main` instead, create a GitHub repository variable named `EDGE_EVER_UPDATE_CHANNEL` with the value `edge`. You can also run the workflow manually and select either channel for that run. + +GitHub may delay scheduled workflows and may disable them for an inactive public repository. If daily updates stop, open the repository's **Actions** tab, enable **Update deployed EdgeEver**, and run it manually once. Do not force-push over update conflicts; resolve them or return to an unmodified deployment repository. + +## Alternative Entry Points + +- Use [AI Agent Cloudflare Deployment](agent-deploy-cloudflare.md) when an agent should perform the same deterministic CLI deployment with custom configuration. +- Use [Cloudflare Manual Deployment](manual-deploy.md) for advanced configuration, troubleshooting, or emergency recovery. + +All three entry points share the same build, migration, Worker deployment, and verification commands. After first installation, they converge on Workers Builds and the same automatic-update workflow. diff --git a/docs/deploy-cloudflare-button.zh-CN.md b/docs/deploy-cloudflare-button.zh-CN.md new file mode 100644 index 00000000..e79ae0dd --- /dev/null +++ b/docs/deploy-cloudflare-button.zh-CN.md @@ -0,0 +1,33 @@ +# 使用 Cloudflare 一键部署 EdgeEver + +**Deploy to Cloudflare** 按钮是推荐的首次安装方式。它会在你的 GitHub 账号中创建仓库,自动创建 Worker、D1 数据库和 R2 存储桶,执行数据库 migration,并将仓库连接到 Cloudflare Workers Builds。 + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/tianma-if/edgeever) + +## 首次安装 + +1. 登录 Cloudflare 和 GitHub,然后打开上方按钮。 +2. 按提示授权 **Cloudflare Workers & Pages** GitHub App。 +3. 选择目标仓库名、Worker 名称、D1 数据库名称和 R2 bucket 名称。 +4. 为 `EDGE_EVER_AUTH_PASSWORD` 设置仅供当前实例使用的强密码。该字段是 Worker Secret,不得提交到 Git。 +5. 保存并部署。Cloudflare 会执行仓库统一的部署流水线:构建、远程 D1 migration、Worker 发布和部署验证。 +6. 打开生成的 `*.workers.dev` 地址,确认 `/api/health` 返回 `200` 和 `"ok": true`,然后使用用户名 `admin` 和刚设置的密码登录。 + +EdgeEver 采用安全关闭策略。D1 migration 或鉴权 Secret 缺失时,实例会返回 `database_not_ready` 或 `auth_not_configured`,不会暴露免登录工作区。 + +## 自动更新 + +生成的仓库会连接 Workers Builds,因此任何推送到 `main` 的提交都会自动构建、迁移、验证并发布。仓库还包含 **Update deployed EdgeEver** 工作流,每天检查一次上游更新。 + +默认 `stable` 通道跟随最新的正式 GitHub Release。发布更新前,工作流会在本地合并并验证依赖安装、本地 D1 migration、自动化测试、类型检查和生产 Web 构建。合并冲突或验证失败时不会修改线上 `main`,并会尝试创建 Issue 提供恢复提示。 + +如果希望跟随上游最新 `main`,请创建名为 `EDGE_EVER_UPDATE_CHANNEL`、值为 `edge` 的 GitHub Repository Variable。也可以手动运行该工作流,并为本次运行选择任一通道。 + +GitHub 的定时任务可能延迟,也可能因公共仓库长期无活动而被停用。如果每日更新停止,请打开仓库的 **Actions** 页面,启用 **Update deployed EdgeEver** 并手动运行一次。遇到更新冲突时不要 force push,应解决冲突或恢复为未定制的部署仓库。 + +## 其他部署入口 + +- 需要由 Agent 使用自定义配置执行同一套确定性 CLI 部署时,使用 [AI Agent Cloudflare Deployment](agent-deploy-cloudflare.md)。 +- 高级配置、故障排查或紧急恢复时,使用 [Cloudflare 手动部署指南](manual-deploy.zh-CN.md)。 + +三个入口共用同一套构建、migration、Worker 发布和验证命令。首次安装完成后,它们都会汇合到 Workers Builds 和同一套自动更新工作流。 diff --git a/docs/manual-deploy.md b/docs/manual-deploy.md index 11a95ff9..87b9b1f9 100644 --- a/docs/manual-deploy.md +++ b/docs/manual-deploy.md @@ -1,105 +1,99 @@ # Cloudflare Manual Deployment Guide -If you are comfortable with Cloudflare and the command line, or prefer customized control over first installation and resource setup, follow this guide for manual deployment. Cloudflare Workers Builds handles routine updates; local deployment is only for first installation and emergency recovery. +Use this path for advanced first installation, custom configuration, troubleshooting, or emergency recovery. Most users should start with [Deploy to Cloudflare](deploy-cloudflare-button.md). An AI assistant must follow the [AI Agent deployment contract](agent-deploy-cloudflare.md), which invokes the same commands documented here. -> 💡 **Tip**: If you are deploying using an AI assistant (such as Claude Code, Codex, Antigravity, or Cursor), the agent should follow the [AI Agent Cloudflare Deployment](https://github.com/tianma-if/edgeever/blob/main/docs/agent-deploy-cloudflare.md) runbook. +All entry points share one deployment core: -## Deployment Steps +```text +build:cloudflare -> db:migrate:remote -> deploy:worker -> deploy:verify +``` -1. **Fork the official repository**: - Visit and fork the official repository: [https://github.com/tianma-if/edgeever](https://github.com/tianma-if/edgeever) +The manual entrypoint adds local configuration checks before that pipeline. Routine updates are handled by Cloudflare Workers Builds rather than a local machine. + +## Automated CLI Setup + +1. Create a GitHub repository from [tianma-if/edgeever](https://github.com/tianma-if/edgeever), then clone it: -2. **Clone your fork**: ```sh - git clone + git clone cd edgeever ``` - Install Node.js 22 or newer and Bun on the deployment machine. Wrangler is provided by the project dependencies and does not need to be installed globally. +2. Install Node.js 22 or newer and Bun. Wrangler is included in the project and does not need a global installation. + +3. Install dependencies and initialize the Cloudflare resources: -3. **Deploy with the automated helper commands**: ```sh - # Copy the configuration template cp .env.local.example .env.local - - # Install dependencies bun install - - # Initialize deployment resources with the default admin / admin123 login bun run deploy:setup - - # Check the deployment environment and configurations bun run deploy:doctor - - # Deploy to Cloudflare - bun run deploy + bun run deploy:manual ``` - To use a custom first-login password, run `EDGE_EVER_PASSWORD='' bun run deploy:setup` instead. You can also change the password later in Personal Settings. + The template uses `admin` / `admin123` for the initial login. To choose a password during setup, run this instead of the plain setup command: - `deploy:setup` always invokes the project-local Wrangler directly, so a global `wrangler` installation is not required and PowerShell, CMD, and Git Bash are supported. If Cloudflare is not authorized yet, the command starts `wrangler login` automatically. If the browser does not open, copy the authorization URL printed in the terminal into a browser and leave the command running until verification completes. + ```sh + EDGE_EVER_PASSWORD='' bun run deploy:setup + ``` -### Creating Cloudflare Resources Manually +`deploy:setup` uses the project-local Wrangler, starts `wrangler login` when authorization is missing, creates or reuses D1 and R2, and writes the resulting non-secret configuration to the git-ignored `.env.local`. `deploy:manual` runs the doctor, production build, common deployment pipeline, and remote verification. -If you prefer not to use the automated `deploy:setup` helper, you can create the resources manually using Cloudflare CLI (Wrangler): +## Creating Resources Manually + +If you do not want `deploy:setup` to create resources, run: ```sh -# Copy configuration template and install dependencies cp .env.local.example .env.local bun install - -# Create the D1 database bunx wrangler d1 create edgeever - -# Create the R2 bucket bunx wrangler r2 bucket create edgeever-resources - -# Edit .env.local and fill in at least the generated resource values -# EDGE_EVER_D1_DATABASE_ID= -# EDGE_EVER_R2_BUCKET_NAME=edgeever-resources -# EDGE_EVER_AUTH_PASSWORD=admin123 -# EDGE_EVER_SESSION_TTL_DAYS=400 - -# Validate the completed configuration before deploying -bun run deploy:doctor -bun run deploy ``` -Before running `bun run deploy`, copy the D1 `database_id` and R2 bucket name into your local `.env.local` file. The template uses `admin` / `admin123` for the initial login; edit `EDGE_EVER_AUTH_PASSWORD` if you prefer another initial password. Keep the session lifetime at the template default of `400` days; the server also caps larger values at 400 days. +Copy the returned D1 ID and the resource names into `.env.local`: -`bun run deploy` builds the web app, applies remote D1 migrations, deploys the Worker, and uploads `EDGE_EVER_AUTH_PASSWORD` as a Worker Secret. After the first successful login, EdgeEver stores a salted PBKDF2-SHA256 hash in D1. Existing installations may continue to use `EDGE_EVER_AUTH_PASSWORD_HASH`; when both Secrets are set, the hash takes precedence. Verify the deployment by signing in with `EDGE_EVER_AUTH_USERNAME` and the configured password. +```text +EDGE_EVER_D1_DATABASE_ID= +EDGE_EVER_R2_BUCKET_NAME=edgeever-resources +EDGE_EVER_AUTH_USERNAME=admin +EDGE_EVER_AUTH_PASSWORD= +EDGE_EVER_SESSION_TTL_DAYS=400 +``` -`.env.local` is read only by the local EdgeEver scripts. It is not automatically uploaded to Cloudflare as a file, and it must never be uploaded manually or committed to Git. A build started directly from Cloudflare Dashboard does not automatically receive the D1, R2, or password settings in this file. Use `bun run deploy` for the first deployment, then use `bun run deploy:builds:setup` to copy the required settings securely into Workers Builds. +Then run: -The deployment command now runs `deploy:doctor` first and stops when the D1 ID, R2 bucket, or login password is missing. Remote D1 migrations are confirmed automatically and require no user input. Before invoking Wrangler, the migration script verifies that local SQL migrations exist, creates an LF-only copy, and always runs Wrangler on its official Node.js runtime; database triggers use the single-line form that remote D1 parses correctly. This prevents Windows commands that migrate no tables, Bun exiting Wrangler early, and remote D1 misparsing triggers as `incomplete input`. After deployment, `deploy:verify` checks the remote D1 schema and the Worker's authentication Secret; a missing requirement makes deployment exit as failed. The Worker also fails closed: when a production instance has neither a password Secret nor an enabled user, it returns `auth_not_configured` instead of opening an unauthenticated workspace. `/api/health` checks D1 and authentication initialization; the instance is ready only when it returns `200` with `"ok": true`. +```sh +bun run deploy:doctor +bun run deploy:manual +``` -Existing installations do not need to migrate. If you intentionally switch from the hash setting to `EDGE_EVER_AUTH_PASSWORD`, remove the old `EDGE_EVER_AUTH_PASSWORD_HASH` from `.env.local`, Workers Builds, and the Worker's runtime Secrets; otherwise the legacy hash remains authoritative. +`.env.local` is read only by local EdgeEver scripts. Never upload it, commit it, or copy it into a Cloudflare build as a file. The standard `bun run deploy` command is reserved for Cloudflare's one-click non-interactive entrypoint; local and Agent deployments use `bun run deploy:manual`. -### Recovering login or database initialization failures +The deployment pipeline applies remote D1 migrations without an interactive confirmation, deploys the Worker, and verifies required tables and the authentication Secret. It normalizes migration SQL to LF and runs Wrangler with its supported Node.js runtime for consistent behavior across Windows, macOS, and Linux. -- If the login page says the database is not ready, or the API returns `database_not_ready`, verify that the D1 binding is named exactly `DB`, then run `bun run db:migrate:remote && bun run deploy`. -- If the login page says the security setup is incomplete, or the API returns `auth_not_configured`, configure `EDGE_EVER_AUTH_PASSWORD` in `.env.local`, then run `bun run deploy`. Never clear the account settings to bypass login. -- If credentials were inserted into the `users` table manually, do not put plaintext in `password_hash`. EdgeEver rejects invalid hashes and points to the safe recovery command on the login page. Run: +EdgeEver fails closed: a production instance without completed D1 migrations or authentication configuration returns `database_not_ready` or `auth_not_configured` instead of exposing an unauthenticated workspace. The instance is ready only when `/api/health` returns `200` with `"ok": true` and login succeeds. + +## Recovery + +- Database not ready: confirm the D1 binding is exactly `DB`, then run `bun run deploy:manual`. +- Authentication not configured: set `EDGE_EVER_AUTH_PASSWORD` in `.env.local`, then run `bun run deploy:manual`. +- Invalid or lost account password: never write plaintext to `users.password_hash`. Run: ```sh EDGE_EVER_PASSWORD='' \ bun run auth:reset-password -- --remote --username admin ``` - This command creates a compatible PBKDF2-SHA256 hash and revokes the account's old sessions without deleting note data. +The binding names must be exactly `DB` for D1 and `RESOURCES` for R2. Existing installations using `EDGE_EVER_AUTH_PASSWORD_HASH` remain supported; when both password Secrets exist, the hash takes precedence. -Cloudflare binding names must be exactly `DB` for D1 and `RESOURCES` for R2. The plain variable `EDGE_EVER_AUTH_USERNAME` does not replace the encrypted `EDGE_EVER_AUTH_PASSWORD` Secret. +## Enable Workers Builds and Automatic Updates ---- - -## Enable Automatic Updates - -After the first deployment, connect the Worker to the fork's `main` branch. Cloudflare Workers Builds is the standard production deployment path for every EdgeEver instance. Follow [Cloudflare Workers Builds](cloudflare-workers-builds.md) to create the configuration-only **User API Token** (not an Account API Token), save it privately as `EDGE_EVER_BUILDS_API_TOKEN` in `.env.local`, then run: +After the first CLI deployment, follow [Cloudflare Workers Builds](cloudflare-workers-builds.md) and run: ```sh bun run deploy:builds:setup ``` -The command configures the Git repository connection, production trigger, build variables, and the deployment token needed for D1 migrations. Afterwards, use **Sync fork** or push to `main`; Cloudflare automatically builds the web app, applies new remote D1 migrations, and deploys the Worker. No GitHub Actions secrets or local redeployment are required. +This connects the Worker to the repository's `main` branch and securely copies the build variables required for future migrations and deployment. Every later push to `main` uses the same common deployment pipeline. -Keep `bun run deploy` available for first installation and emergency recovery. +The repository's **Update deployed EdgeEver** workflow checks upstream formal Releases daily on the default `stable` channel. Set the GitHub repository variable `EDGE_EVER_UPDATE_CHANNEL=edge` to follow upstream `main` instead. GitHub disables scheduled workflows by default on public forks, so open **Actions** and enable this workflow after a fork-based installation. Update conflicts or failed local verification leave the deployed branch unchanged. diff --git a/docs/manual-deploy.zh-CN.md b/docs/manual-deploy.zh-CN.md index 20282dc5..acda1ea7 100644 --- a/docs/manual-deploy.zh-CN.md +++ b/docs/manual-deploy.zh-CN.md @@ -1,105 +1,99 @@ # Cloudflare 手动部署指南 -如果你熟悉 Cloudflare 和命令行,或者想自定义/精细控制首次安装与资源配置,可以按照以下指南进行手动部署。日常更新统一由 Cloudflare Workers Builds 完成;本地部署仅用于首次安装和紧急修复。 +该入口用于高级首次安装、自定义配置、故障排查和紧急恢复。大多数用户应优先使用 [Cloudflare 一键部署](deploy-cloudflare-button.zh-CN.md)。AI 助手必须遵循 [AI Agent 部署约定](agent-deploy-cloudflare.md),其底层调用的也是本文命令。 -> 💡 **提示**:如果是通过 AI 助手(Claude Code、Codex、Antigravity、Cursor 等)进行部署,AI 助手应优先参考 [AI Agent Cloudflare Deployment](https://github.com/tianma-if/edgeever/blob/main/docs/agent-deploy-cloudflare.md) 约定。 +所有入口共用同一套部署内核: -## 部署步骤 +```text +build:cloudflare -> db:migrate:remote -> deploy:worker -> deploy:verify +``` -1. **Fork 官方仓库**: - 访问并 Fork 官方仓库:[https://github.com/tianma-if/edgeever](https://github.com/tianma-if/edgeever) +手动入口只是在该流水线外增加本地配置检查。日常更新统一由 Cloudflare Workers Builds 执行,不依赖本地电脑。 + +## 自动化 CLI 初始化 + +1. 基于 [tianma-if/edgeever](https://github.com/tianma-if/edgeever) 创建 GitHub 仓库,然后克隆: -2. **Clone 你的 Fork 仓库**: ```sh - git clone <你的 Fork 仓库 URL> + git clone <你的仓库 URL> cd edgeever ``` - 本机需安装 Node.js 22 或更高版本以及 Bun。Wrangler 由项目依赖提供,无需全局安装。 +2. 安装 Node.js 22 或更高版本以及 Bun。Wrangler 已包含在项目依赖中,无需全局安装。 + +3. 安装依赖并初始化 Cloudflare 资源: -3. **使用自动化辅助命令部署**: ```sh - # 复制配置文件模板 cp .env.local.example .env.local - - # 安装依赖 bun install - - # 执行部署初始化,默认登录账号密码为 admin / admin123 bun run deploy:setup - - # 诊断部署环境与配置 bun run deploy:doctor - - # 执行部署 - bun run deploy + bun run deploy:manual ``` - 如果希望首次登录就使用自定义密码,可改为执行 `EDGE_EVER_PASSWORD='<你的密码>' bun run deploy:setup`。部署成功后也可以在个人中心修改密码。 + 模板使用 `admin` / `admin123` 作为初始登录账号密码。如果希望初始化时指定密码,请用下面的命令替代普通 setup: - `deploy:setup` 始终直接使用项目内安装的 Wrangler,不要求全局安装 `wrangler`,并兼容 PowerShell、CMD 与 Git Bash。若尚未授权 Cloudflare,该命令会自动启动 `wrangler login`;浏览器没有自动打开时,请复制终端显示的授权链接到浏览器完成登录,然后保持命令运行到验证结束。 + ```sh + EDGE_EVER_PASSWORD='<你的密码>' bun run deploy:setup + ``` -### 完全手动创建 Cloudflare 资源 +`deploy:setup` 使用项目内置 Wrangler;缺少授权时启动 `wrangler login`,创建或复用 D1、R2,并把非 Secret 配置写入 Git 忽略的 `.env.local`。`deploy:manual` 会执行 doctor、生产构建、统一部署流水线和远端验证。 -如果你不想使用 `deploy:setup` 自动化脚本,也可以完全手动使用 Cloudflare CLI (Wrangler) 创建资源: +## 完全手动创建资源 + +如果不希望 `deploy:setup` 创建资源,请执行: ```sh -# 复制配置文件模板并安装依赖 cp .env.local.example .env.local bun install - -# 手动创建 D1 数据库 bunx wrangler d1 create edgeever - -# 手动创建 R2 存储桶 bunx wrangler r2 bucket create edgeever-resources - -# 编辑 .env.local,至少填入刚创建的资源配置 -# EDGE_EVER_D1_DATABASE_ID= -# EDGE_EVER_R2_BUCKET_NAME=edgeever-resources -# EDGE_EVER_AUTH_PASSWORD=admin123 -# EDGE_EVER_SESSION_TTL_DAYS=400 - -# 确认配置完整后再部署 -bun run deploy:doctor -bun run deploy ``` -必须在执行 `bun run deploy` **之前**,将 D1 创建命令返回的 `database_id` 和 R2 bucket 名称写入本机 `.env.local`。模板的初始登录账号密码为 `admin` / `admin123`;如需使用其他初始密码,可修改 `EDGE_EVER_AUTH_PASSWORD`。会话有效期建议保留模板中的 `400` 天;服务端也会把更大的值限制为 400 天。 +将返回的 D1 ID 和资源名称写入 `.env.local`: -`bun run deploy` 会构建 Web 应用、执行远程 D1 migration、部署 Worker,并将 `EDGE_EVER_AUTH_PASSWORD` 作为 Worker Secret 上传。首次登录成功后,EdgeEver 会将加盐的 PBKDF2-SHA256 哈希写入 D1。已有实例可以继续使用 `EDGE_EVER_AUTH_PASSWORD_HASH`;两个 Secret 同时存在时优先使用哈希。部署完成后,请使用 `.env.local` 中的 `EDGE_EVER_AUTH_USERNAME` 和配置的密码登录验证。 +```text +EDGE_EVER_D1_DATABASE_ID= +EDGE_EVER_R2_BUCKET_NAME=edgeever-resources +EDGE_EVER_AUTH_USERNAME=admin +EDGE_EVER_AUTH_PASSWORD=<强密码> +EDGE_EVER_SESSION_TTL_DAYS=400 +``` -`.env.local` 只供本机 EdgeEver 脚本读取,不会作为文件自动上传到 Cloudflare,也不应手工上传或提交到 Git。若从 Cloudflare Dashboard 直接触发构建,该文件中的 D1、R2 和密码配置不会自动生效;请使用 `bun run deploy` 完成首次部署,再使用 `bun run deploy:builds:setup` 将必要配置安全地写入 Workers Builds。 +然后执行: -部署命令会先自动执行 `deploy:doctor`,缺少 D1 ID、R2 bucket 或登录密码时会直接停止。远程 D1 migration 会自动确认执行,不需要用户输入。迁移脚本还会在调用 Wrangler 前确认本地 SQL migration 存在、生成统一使用 LF 换行的副本,并让 Wrangler 始终运行在其官方 Node.js runtime 下;数据库 Trigger 使用远端 D1 可正确解析的单行格式。这样可以避免 Windows 上未迁移任何表、Bun 提前退出 Wrangler 进程或远端 D1 将 Trigger 误报为 `incomplete input`。部署完成后还会执行 `deploy:verify`,检查远程 D1 的关键数据表和 Worker 鉴权 Secret;任一缺失都会让部署以失败退出。Worker 也采用安全关闭策略:生产实例既没有密码 Secret、又没有有效用户时,会返回 `auth_not_configured`,不会降级成免登录工作区。`/api/health` 会检查 D1 和鉴权初始化状态;只有返回 `200` 且包含 `"ok": true` 才表示实例真正可用。 +```sh +bun run deploy:doctor +bun run deploy:manual +``` -已有实例无需迁移。如果确实要从哈希配置切换为 `EDGE_EVER_AUTH_PASSWORD`,需要同时从 `.env.local`、Workers Builds 和 Worker 运行时 Secrets 中移除旧的 `EDGE_EVER_AUTH_PASSWORD_HASH`,否则旧哈希仍会优先生效。 +`.env.local` 仅供本地 EdgeEver 脚本读取。不得上传、提交到 Git,或作为文件复制到 Cloudflare 构建环境。标准的 `bun run deploy` 专供 Cloudflare 一键部署的非交互入口使用;本地和 Agent 部署使用 `bun run deploy:manual`。 -### 登录或数据库初始化故障恢复 +部署流水线会非交互执行远程 D1 migration、发布 Worker,并验证必需数据表和鉴权 Secret。迁移 SQL 会统一为 LF,Wrangler 始终通过其支持的 Node.js runtime 运行,确保 Windows、macOS 和 Linux 行为一致。 -- 登录页显示“数据库尚未就绪”或接口返回 `database_not_ready`:确认 D1 binding 名称严格为 `DB`,然后执行 `bun run db:migrate:remote && bun run deploy`。 -- 登录页显示“实例尚未完成安全配置”或接口返回 `auth_not_configured`:在 `.env.local` 配置 `EDGE_EVER_AUTH_PASSWORD`,然后执行 `bun run deploy`。不要通过清空账号配置绕过登录。 -- 已经手工向 `users` 表写入账号密码:不要向 `password_hash` 写明文。EdgeEver 会拒绝无效哈希并在登录页提示安全恢复命令,请执行: +EdgeEver 采用安全关闭策略:生产实例未完成 D1 migration 或鉴权配置时会返回 `database_not_ready` 或 `auth_not_configured`,不会暴露免登录工作区。只有 `/api/health` 返回 `200` 和 `"ok": true`,且登录成功,实例才算可用。 + +## 故障恢复 + +- 数据库未就绪:确认 D1 binding 名称严格为 `DB`,然后运行 `bun run deploy:manual`。 +- 鉴权未配置:在 `.env.local` 设置 `EDGE_EVER_AUTH_PASSWORD`,然后运行 `bun run deploy:manual`。 +- 账号密码无效或丢失:不要向 `users.password_hash` 写入明文,请执行: ```sh EDGE_EVER_PASSWORD='<至少 8 位的新密码>' \ bun run auth:reset-password -- --remote --username admin ``` - 该命令会生成兼容的 PBKDF2-SHA256 哈希并撤销该账号的旧会话,不会删除笔记数据。 +D1 和 R2 的 binding 名称必须分别为 `DB` 和 `RESOURCES`。已有实例仍可使用 `EDGE_EVER_AUTH_PASSWORD_HASH`;两个密码 Secret 同时存在时优先使用 hash。 -Cloudflare 控制台中的 binding 名称必须严格使用 `DB`(D1)和 `RESOURCES`(R2)。普通变量 `EDGE_EVER_AUTH_USERNAME` 不能代替加密 Secret `EDGE_EVER_AUTH_PASSWORD`。 +## 开启 Workers Builds 和自动更新 ---- - -## 开启自动更新 - -首次部署后,必须将 Worker 连接到 Fork 的 `main` 分支;Cloudflare Workers Builds 是所有 EdgeEver 实例的标准生产发布路径。请先按 [Cloudflare Workers Builds 自动部署](cloudflare-workers-builds.zh-CN.md) 创建仅供配置使用的 **User API Token**(不是 Account API Token),将它私下写入 `.env.local` 的 `EDGE_EVER_BUILDS_API_TOKEN`,然后执行: +CLI 首次部署完成后,按照 [Cloudflare Workers Builds 自动部署](cloudflare-workers-builds.zh-CN.md) 执行: ```sh bun run deploy:builds:setup ``` -该命令会配置 Git 仓库连接、生产触发器、构建变量和 D1 migration 所需的部署 token。完成后,只需在 Fork 页面点击 **Sync fork**,或推送到 `main`;Cloudflare 会自动构建 Web、应用新的远程 D1 migration 并发布 Worker。不需要 GitHub Actions Secrets,也不需要本地重新部署。 +该命令会把 Worker 连接到仓库 `main` 分支,并安全复制后续 migration 和部署所需的构建变量。此后任何推送到 `main` 的提交都会使用同一套部署内核。 -请保留 `bun run deploy` 作为首次安装和紧急修复的入口。 +仓库中的 **Update deployed EdgeEver** 工作流默认使用 `stable` 通道,每天检查上游正式 Release。设置 GitHub Repository Variable `EDGE_EVER_UPDATE_CHANNEL=edge` 后可改为跟随上游 `main`。GitHub 默认会禁用公共 Fork 中的定时工作流,因此通过 Fork 安装后需要打开 **Actions** 并启用该工作流。更新冲突或本地验证失败时不会修改已部署分支。 diff --git a/docs/promotion/promotion_article_juejin.md b/docs/promotion/promotion_article_juejin.md index 16ba6c28..3f9f030e 100644 --- a/docs/promotion/promotion_article_juejin.md +++ b/docs/promotion/promotion_article_juejin.md @@ -112,7 +112,7 @@ EDGE_EVER_PASSWORD='你的后台登录密码' bun run deploy:setup # 3. 环境自检并部署 bun run deploy:doctor -bun run deploy +bun run deploy:manual ``` 部署完成后,终端会直接输出你的独立访问域名,点开即可使用! diff --git a/package.json b/package.json index b05ddf42..9dc451af 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,11 @@ "mcp:stdio": "bun scripts/edgeever-mcp-stdio.mjs", "deploy:doctor": "bun scripts/cloudflare-deploy.mjs doctor", "deploy:setup": "bun scripts/cloudflare-deploy.mjs setup", - "deploy": "bun run deploy:doctor && bun run build && bun run deploy:worker", - "deploy:worker": "bun run deploy:cloudflare-builds", - "deploy:cloudflare-builds": "bun run db:migrate:remote && bun scripts/run-wrangler.mjs deploy && bun run deploy:verify", + "deploy": "bun run build:cloudflare && EDGE_EVER_USE_EXISTING_AUTH_SECRET=true bun run deploy:ci", + "deploy:manual": "bun run deploy:doctor && bun run build:cloudflare && bun run deploy:ci", + "deploy:ci": "bun run db:migrate:remote && bun run deploy:worker && bun run deploy:verify", + "deploy:worker": "bun scripts/run-wrangler.mjs deploy", + "deploy:cloudflare-builds": "bun run deploy:ci", "deploy:verify": "bun scripts/verify-deployment.mjs", "deploy:builds:setup": "bun scripts/cloudflare-workers-builds.mjs setup", "deploy:site": "bun run build:site && wrangler pages deploy apps/site/dist --project-name=edgeever-official" @@ -57,6 +59,9 @@ }, "RESOURCES": { "description": "EdgeEver 的 R2 对象存储桶。用于存储笔记内图片、附件和未来导入导出文件;D1 只保存资源元数据和 memo 关联。" + }, + "EDGE_EVER_AUTH_PASSWORD": { + "description": "首次登录密码,必填。请设置一个仅用于此实例的强密码;部署后可在个人设置中修改。" } } }, diff --git a/scripts/cloudflare-workers-builds.mjs b/scripts/cloudflare-workers-builds.mjs index 149060cf..b7980de6 100644 --- a/scripts/cloudflare-workers-builds.mjs +++ b/scripts/cloudflare-workers-builds.mjs @@ -260,7 +260,7 @@ const setup = async () => { const scripts = await request("GET", `/accounts/${accountId}/workers/scripts`); const worker = scripts?.find((script) => script.id === workerName); if (!worker?.tag) { - throw new Error(`Worker ${workerName} was not found. Complete the first bun run deploy before enabling Workers Builds.`); + throw new Error(`Worker ${workerName} was not found. Complete the first bun run deploy:manual before enabling Workers Builds.`); } const buildTokens = await request("GET", `/accounts/${accountId}/builds/tokens`); diff --git a/scripts/run-wrangler.mjs b/scripts/run-wrangler.mjs index 9d67b9cb..fbcaac63 100644 --- a/scripts/run-wrangler.mjs +++ b/scripts/run-wrangler.mjs @@ -283,14 +283,19 @@ const authSecrets = { ...(authPasswordHash ? { EDGE_EVER_AUTH_PASSWORD_HASH: authPasswordHash } : {}), }; const finalWranglerArgs = [...wranglerArgs]; +const useExistingAuthSecret = process.env.EDGE_EVER_USE_EXISTING_AUTH_SECRET?.trim().toLowerCase() === "true"; -if (isDeployCommand && Object.keys(authSecrets).length === 0) { +if (isDeployCommand && Object.keys(authSecrets).length === 0 && !useExistingAuthSecret) { console.error( - "Refusing to deploy without EDGE_EVER_AUTH_PASSWORD or EDGE_EVER_AUTH_PASSWORD_HASH. Run bun run deploy:setup first.", + "Refusing to deploy without EDGE_EVER_AUTH_PASSWORD or EDGE_EVER_AUTH_PASSWORD_HASH. Run bun run deploy:setup first, or use the Cloudflare one-click deploy entrypoint.", ); process.exit(1); } +if (isDeployCommand && Object.keys(authSecrets).length === 0 && useExistingAuthSecret) { + console.log("[info] using the authentication Secret provisioned by Cloudflare"); +} + if (isLocalDevCommand && !hasEnvFileArg) { writeFileSync(generatedLocalDevEnvPath, "# Intentionally empty: local development must not inherit remote instance secrets.\n"); finalWranglerArgs.push("--env-file", generatedLocalDevEnvPath); diff --git a/scripts/verify-deployment.mjs b/scripts/verify-deployment.mjs index 4d1d12aa..50eebff6 100644 --- a/scripts/verify-deployment.mjs +++ b/scripts/verify-deployment.mjs @@ -61,7 +61,7 @@ const runWrangler = (args, options = {}) => { if (!result.stdout.trim() && !options.allowEmptyOutput) { if (result.stderr) process.stderr.write(result.stderr); throw new Error( - "Wrangler completed without returning verification data. Re-run bun run deploy:doctor, then retry.", + "Wrangler completed without returning verification data. Check the D1 binding and deployment credentials, then retry.", ); } diff --git a/tests/deployment-entrypoints.test.ts b/tests/deployment-entrypoints.test.ts new file mode 100644 index 00000000..a7d43694 --- /dev/null +++ b/tests/deployment-entrypoints.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const repositoryRoot = resolve(import.meta.dir, ".."); +const readRepositoryFile = (path: string) => readFileSync(resolve(repositoryRoot, path), "utf8"); + +describe("Cloudflare deployment entrypoints", () => { + test("all entrypoints converge on the common deployment pipeline", () => { + const packageJson = JSON.parse(readRepositoryFile("package.json")); + const scripts = packageJson.scripts as Record; + + expect(scripts.deploy).toContain("bun run build:cloudflare"); + expect(scripts.deploy).toContain("EDGE_EVER_USE_EXISTING_AUTH_SECRET=true"); + expect(scripts.deploy).toContain("deploy:ci"); + expect(scripts["deploy:manual"]).toBe( + "bun run deploy:doctor && bun run build:cloudflare && bun run deploy:ci", + ); + expect(scripts["deploy:ci"]).toBe( + "bun run db:migrate:remote && bun run deploy:worker && bun run deploy:verify", + ); + expect(scripts["deploy:cloudflare-builds"]).toBe("bun run deploy:ci"); + }); + + test("one-click deployment declares the required authentication Secret", () => { + const example = readRepositoryFile(".dev.vars.example"); + expect(example).toMatch(/^EDGE_EVER_AUTH_PASSWORD=\s*$/m); + + const packageJson = JSON.parse(readRepositoryFile("package.json")); + expect(packageJson.cloudflare.bindings.EDGE_EVER_AUTH_PASSWORD.description).toBeTruthy(); + }); + + test("deployed repositories receive guarded daily upstream updates", () => { + const workflow = readRepositoryFile(".github/workflows/sync-edgeever-upstream.yml"); + + expect(workflow).toContain("github.repository != 'tianma-if/edgeever'"); + expect(workflow).toContain("UPSTREAM_REPOSITORY: tianma-if/edgeever"); + expect(workflow).toContain("EDGE_EVER_UPDATE_CHANNEL"); + expect(workflow).toContain("stable)"); + expect(workflow).toContain("edge)"); + expect(workflow).toContain("bun run db:migrate:local"); + expect(workflow).toContain("bun test"); + expect(workflow).toContain("git push origin HEAD:main"); + }); +});