From 95f237c957a460bbe4482d6c6e24bd6e0175801d Mon Sep 17 00:00:00 2001 From: imengying Date: Mon, 10 Aug 2026 21:24:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcloudflare=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++++++++++++------- scripts/build-agent.sh | 3 +++ scripts/build-worker.sh | 3 +++ scripts/deploy-worker.sh | 45 ++++++++++++++++++++++++++++++++++++++-- scripts/ensure-rust.sh | 41 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 +++---- 6 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 scripts/ensure-rust.sh diff --git a/README.md b/README.md index 7b91003..775cca7 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,24 @@ - 节点管理、批量删除、拖拽排序、Agent 密钥轮换和在线配置 - Telegram 通知、资源告警、离线/到期提醒和数据维护 - 内置主题与 CFSM Theme Store,支持主题预览、应用和自定义设置 -- 多站点聚合,支持最多 16 个公开 CF Monitor 站点 - Rust Agent 支持 Linux x86_64/ARM64、Windows x86_64 和 macOS ARM64,可按节点自动更新 ## 部署到 Cloudflare -1. Fork 本项目,在 Cloudflare Dashboard 打开 **Workers & Pages**,选择 **Continue with GitHub** 并导入仓库。 +1. Fork 本项目,打开 [Workers & Pages](https://dash.cloudflare.com/?to=/:account/workers-and-pages/create),选择 **Continue with GitHub** 并导入仓库。 2. 使用生产分支 `main`,根目录 `/`,构建命令留空,部署命令填写 `bun run deploy`。 -3. 在 Worker 的 **Settings → Variables and Secrets** 中设置 `ADMIN_PASSWORD`;用户名默认为 `admin`,也可以设置 `ADMIN_USERNAME`。`SESSION_SECRET`、Cloudflare 用量查询凭据按需添加。 +3. 在创建页展开 **高级设置**,按下表添加变量。 -| 变量 | 默认值 | 说明 | +| 变量 | 必填/可选 | 说明 | | --- | ---: | --- | -| `SITE_NAME` | `CF Monitor` | 站点名称 | -| `OFFLINE_THRESHOLD_SECONDS` | `180` | 离线判定秒数,范围 30-3600 | -| `HISTORY_RETENTION_DAYS` | `30` | 历史保留天数,范围 1-365 | +| `ADMIN_USERNAME` | 可选 | 管理员登录用户名,未设置时使用 `admin` | +| `ADMIN_PASSWORD` | 必填 | 初始管理员密码,勾选“加密” | +| `SESSION_SECRET` | 可选 | 会话签名密钥,勾选“加密”;未设置时使用密码哈希 | +| `SITE_NAME` | 可选 | 站点名称,未设置时使用 `CF Monitor` | +| `OFFLINE_THRESHOLD_SECONDS` | 可选 | 离线判定秒数,未设置时使用 180,范围 30-3600 | +| `HISTORY_RETENTION_DAYS` | 可选 | 历史保留天数,未设置时使用 30,范围 1-365 | +| `CF_USAGE_ACCOUNT_ID` | 可选 | Cloudflare 用量查询的账户 ID,不设置则不启用用量查询 | +| `CF_USAGE_API_TOKEN` | 可选 | 用量查询 Token,勾选“加密”;需要 Account Analytics Read 权限 | ## API @@ -63,6 +67,10 @@ 第三方主题可在构建目录提供 `theme-settings.json`(`schema: 1`)。后台按字段生成设置控件,保存值通过 `/api/config` 的 `theme_options` 返回。 +## 鸣谢 + +感谢 [CF-Server-Monitor](https://github.com/huilang-me/CF-Server-Monitor) 项目提供的思路。 + ## License MIT diff --git a/scripts/build-agent.sh b/scripts/build-agent.sh index 67473b3..14b41d7 100644 --- a/scripts/build-agent.sh +++ b/scripts/build-agent.sh @@ -1,6 +1,9 @@ #!/bin/sh set -eu +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +. "$script_dir/ensure-rust.sh" + case "$(uname -m)" in x86_64|amd64) arch="x86_64" ;; aarch64|arm64) arch="aarch64" ;; diff --git a/scripts/build-worker.sh b/scripts/build-worker.sh index ce4f751..a00ac3a 100644 --- a/scripts/build-worker.sh +++ b/scripts/build-worker.sh @@ -1,6 +1,9 @@ #!/bin/sh set -eu +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +. "$script_dir/ensure-rust.sh" + if [ "${CF_MONITOR_ADMIN_READY:-0}" != "1" ]; then bun run build:agent bun run build:frontend diff --git a/scripts/deploy-worker.sh b/scripts/deploy-worker.sh index f9530ba..f7280ad 100644 --- a/scripts/deploy-worker.sh +++ b/scripts/deploy-worker.sh @@ -1,12 +1,53 @@ #!/bin/sh set -eu +runtime_secrets_file="" + +cleanup() { + if [ -n "$runtime_secrets_file" ]; then + rm -f "$runtime_secrets_file" + fi +} + +trap cleanup EXIT HUP INT TERM + +prepare_runtime_secrets() { + runtime_secrets_file=$(mktemp) + RUNTIME_SECRETS_FILE="$runtime_secrets_file" bun -e ' + const names = ["ADMIN_PASSWORD", "SESSION_SECRET", "CF_USAGE_API_TOKEN"]; + const secrets = Object.fromEntries( + names.flatMap((name) => process.env[name] ? [[name, process.env[name]]] : []), + ); + if (Object.keys(secrets).length > 0) { + await Bun.write(process.env.RUNTIME_SECRETS_FILE, JSON.stringify(secrets)); + } + ' + + if [ ! -s "$runtime_secrets_file" ]; then + rm -f "$runtime_secrets_file" + runtime_secrets_file="" + fi +} + +deploy_worker() { + set -- bunx wrangler deploy + [ -z "${ADMIN_USERNAME:-}" ] || set -- "$@" --var "ADMIN_USERNAME:${ADMIN_USERNAME}" + [ -z "${SITE_NAME:-}" ] || set -- "$@" --var "SITE_NAME:${SITE_NAME}" + [ -z "${OFFLINE_THRESHOLD_SECONDS:-}" ] || set -- "$@" --var "OFFLINE_THRESHOLD_SECONDS:${OFFLINE_THRESHOLD_SECONDS}" + [ -z "${HISTORY_RETENTION_DAYS:-}" ] || set -- "$@" --var "HISTORY_RETENTION_DAYS:${HISTORY_RETENTION_DAYS}" + [ -z "${CF_USAGE_ACCOUNT_ID:-}" ] || set -- "$@" --var "CF_USAGE_ACCOUNT_ID:${CF_USAGE_ACCOUNT_ID}" + [ -z "$runtime_secrets_file" ] || set -- "$@" --secrets-file "$runtime_secrets_file" + "$@" +} + +prepare_runtime_secrets + # Some Cloudflare deployment flows provision bindings before this script runs. # A first deployment without that pre-provisioning needs one upload to create D1. if ! bunx wrangler d1 info DB --json >/dev/null 2>&1; then echo "D1 binding is not provisioned; creating it through Wrangler deploy..." - bunx wrangler deploy + deploy_worker fi bunx wrangler d1 migrations apply DB --remote -exec bunx wrangler deploy +deploy_worker diff --git a/scripts/ensure-rust.sh b/scripts/ensure-rust.sh new file mode 100644 index 0000000..c179fff --- /dev/null +++ b/scripts/ensure-rust.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -eu + +wasm_target_ready() { + command -v rustc >/dev/null 2>&1 || return 1 + target_libdir=$(rustc --print target-libdir --target wasm32-unknown-unknown 2>/dev/null) || return 1 + test -d "$target_libdir" +} + +if ! command -v cargo >/dev/null 2>&1 || ! wasm_target_ready; then + if ! command -v rustup >/dev/null 2>&1; then + rustup_install_url="https://sh.rustup.rs" + rustup_installer=$(mktemp) + trap 'rm -f "$rustup_installer"' 0 HUP INT TERM + echo "Rust toolchain not found; installing stable Rust..." + if command -v curl >/dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf "$rustup_install_url" -o "$rustup_installer" + elif command -v wget >/dev/null 2>&1; then + wget -qO "$rustup_installer" "$rustup_install_url" + else + echo "Rust is missing and curl/wget is unavailable" >&2 + exit 1 + fi + sh "$rustup_installer" -y --profile minimal --default-toolchain stable + rm -f "$rustup_installer" + trap - 0 HUP INT TERM + fi + + rust_bin_dir="${CARGO_HOME:-$HOME/.cargo}/bin" + export PATH="$rust_bin_dir:$PATH" + if ! command -v cargo >/dev/null 2>&1; then + rustup toolchain install stable --profile minimal + rustup default stable + fi + rustup target add wasm32-unknown-unknown +fi + +if ! command -v cargo >/dev/null 2>&1 || ! wasm_target_ready; then + echo "Rust Cargo or wasm32-unknown-unknown target is unavailable" >&2 + exit 1 +fi diff --git a/src/lib.rs b/src/lib.rs index 1b3bcd3..c15c061 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1574,16 +1574,16 @@ async fn handle(mut req: Request, env: Env, ctx: Context) -> Result { if method == Method::Get && path == "/api/admin/cloudflare-usage" { let account_id = if settings.cloudflare_account_id.trim().is_empty() { - env.secret("CLOUDFLARE_ACCOUNT_ID") - .or_else(|_| env.var("CLOUDFLARE_ACCOUNT_ID")) + env.secret("CF_USAGE_ACCOUNT_ID") + .or_else(|_| env.var("CF_USAGE_ACCOUNT_ID")) .map(|value| value.to_string()) .unwrap_or_default() } else { settings.cloudflare_account_id.clone() }; let token = if settings.cloudflare_api_token.trim().is_empty() { - env.secret("CLOUDFLARE_API_TOKEN") - .or_else(|_| env.var("CLOUDFLARE_API_TOKEN")) + env.secret("CF_USAGE_API_TOKEN") + .or_else(|_| env.var("CF_USAGE_API_TOKEN")) .map(|value| value.to_string()) .unwrap_or_default() } else {