Improve AI agent Cloudflare deployment
This commit is contained in:
1 parent
761dbd8f7f
commit
dca1762c68
7 files changed
+494
-9
No files matched your search
@@ -14,6 +14,9 @@ CLOUDFLARE_API_TOKEN=
|
||||
# 默认会部署为名叫 edgeever 的 Cloudflare Worker。
|
||||
EDGE_EVER_WORKER_NAME=edgeever
|
||||
|
||||
# 是否启用 workers.dev 子域名。可选值:true / false。
|
||||
EDGE_EVER_WORKERS_DEV=true
|
||||
|
||||
# D1 数据库名称。
|
||||
# 创建命令:`bunx wrangler d1 create edgeever`
|
||||
EDGE_EVER_D1_DATABASE_NAME=edgeever
|
||||
@@ -44,6 +47,14 @@ EDGE_EVER_AUTH_PASSWORD_HASH=
|
||||
# Web session 有效期,单位:天。服务端会限制在 1-90 天。
|
||||
EDGE_EVER_SESSION_TTL_DAYS=30
|
||||
|
||||
# 可选:自定义域名。设置后部署脚本会生成 custom_domain route。
|
||||
# 例如:notes.example.com
|
||||
EDGE_EVER_CUSTOM_DOMAIN=
|
||||
|
||||
# 可选:完整 route pattern。未设置时会回退使用 EDGE_EVER_CUSTOM_DOMAIN。
|
||||
# 例如:notes.example.com/*
|
||||
EDGE_EVER_ROUTE_PATTERN=
|
||||
|
||||
# 前端访问 API 的基础地址。
|
||||
# 本地开发默认指向 Wrangler dev:`bun run dev:api`
|
||||
VITE_API_BASE_URL=http://127.0.0.1:8787
|
||||
|
||||
@@ -10,6 +10,23 @@
|
||||
|
||||
严禁创建新的 Git 分支;所有修改、提交和推送都必须直接在 `main` 分支上完成。
|
||||
|
||||
## Cloudflare 自动部署约束
|
||||
|
||||
当用户要求“根据 GitHub 项目链接把本项目安装部署到 Cloudflare”时,应优先按 `docs/agent-deploy-cloudflare.md` 执行。
|
||||
|
||||
推荐流程:
|
||||
|
||||
```sh
|
||||
bun install
|
||||
EDGE_EVER_PASSWORD='<首次登录密码>' bun run deploy:setup
|
||||
bun run deploy:doctor
|
||||
bun run deploy
|
||||
```
|
||||
|
||||
如果用户没有提供首次登录密码,应只询问这一个必要信息,或在用户同意后生成随机密码。Cloudflare 授权、账号、D1/R2 资源、Worker 名称、自定义域名等私有配置必须来自用户环境、Cloudflare MCP/插件、Wrangler 登录态或 `.env.local`,严禁硬编码到仓库文件。
|
||||
|
||||
部署脚本必须通过 `scripts/run-wrangler.mjs` 读取 `.env.local` 并生成临时 Wrangler 配置。不要直接修改 `wrangler.toml` 来写入个人 `database_id`、bucket 名称、Worker 名称或 route。
|
||||
|
||||
## 本地启动约束
|
||||
|
||||
本地预览或调试时,必须优先使用 `bun run dev` 启动完整开发环境,让 API 通过 `scripts/run-wrangler.mjs` 读取 `.env.local` 中的个性化实例配置。实例名称、D1/R2 资源、账号等本机私有配置均以 `.env.local` 为准,严禁在代理指令或代码中硬编码个人实例名。
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
|
||||
EdgeEver 是一个开源、自托管、Cloudflare-native 的现代笔记工作区。它保留经典印象笔记的三栏体验,同时提供清晰的数据模型、REST API、OpenAPI schema 和 MCP endpoint。
|
||||
|
||||
<p align="center">
|
||||
<a href="https://deploy.workers.cloudflare.com/?url=https://github.com/msh01/edgeever">
|
||||
<img src="https://deploy.workers.cloudflare.com/button" alt="Deploy to Cloudflare" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 在线演示
|
||||
|
||||
- Demo 地址:[https://demo.edgeever.org](https://demo.edgeever.org)
|
||||
@@ -75,19 +69,36 @@ bun run build
|
||||
|
||||
## 部署
|
||||
|
||||
最简单的方式是点击上方 **Deploy to Cloudflare** 按钮,根据 Cloudflare 向导完成授权和部署。
|
||||
### AI Agent 一句话部署
|
||||
|
||||
如果你使用 Codex、Claude Code、Antigravity 等 AI Agent,并且已经登录 Cloudflare 或安装了 Cloudflare 相关插件,可以直接把下面这句话发给 Agent:
|
||||
|
||||
```text
|
||||
这个是 GitHub 上的项目链接:https://github.com/msh01/edgeever。请把这个项目安装部署到 Cloudflare 上。
|
||||
```
|
||||
|
||||
Agent 应优先按 [AI Agent Cloudflare Deployment](docs/agent-deploy-cloudflare.md) 执行,只在需要 Cloudflare 授权、首次登录密码或自定义域名时询问你。
|
||||
|
||||
### 手动部署
|
||||
|
||||
如果使用 CLI 部署:
|
||||
|
||||
```sh
|
||||
cp .env.local.example .env.local
|
||||
bun install
|
||||
bunx wrangler d1 create edgeever
|
||||
bunx wrangler r2 bucket create edgeever-resources
|
||||
bun run auth:hash -- <你的密码>
|
||||
bun run deploy
|
||||
```
|
||||
|
||||
把 D1 创建命令返回的 `database_id` 和密码 hash 填入本机 `.env.local`。
|
||||
把 D1 创建命令返回的 `database_id` 和密码 hash 填入本机 `.env.local`。也可以使用自动化辅助命令:
|
||||
|
||||
```sh
|
||||
EDGE_EVER_PASSWORD='<你的密码>' bun run deploy:setup
|
||||
bun run deploy:doctor
|
||||
bun run deploy
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# AI Agent Cloudflare Deployment
|
||||
|
||||
This document is the deployment contract for AI agents. If a user asks an agent to deploy EdgeEver from a GitHub URL to Cloudflare, follow this runbook before asking the user for anything.
|
||||
|
||||
## One Prompt Install
|
||||
|
||||
Users should be able to start with only this prompt:
|
||||
|
||||
```text
|
||||
This is the GitHub project URL: <repo-url>. Install and deploy this project to Cloudflare for me.
|
||||
```
|
||||
|
||||
Chinese version:
|
||||
|
||||
```text
|
||||
这个是 GitHub 上的项目链接:<repo-url>。请把这个项目安装部署到 Cloudflare 上。
|
||||
```
|
||||
|
||||
## Agent Rules
|
||||
|
||||
- Read `AGENTS.md`, `README.md`, `.env.local.example`, and this file first.
|
||||
- Do not create a new Git branch. Work on `main`.
|
||||
- Prefer `bun run deploy:setup`, `bun run deploy:doctor`, and `bun run deploy`.
|
||||
- Do not hard-code a personal Worker name, D1 database ID, R2 bucket name, account ID, API token, or domain in source files.
|
||||
- Use `.env.local` for local/private deployment values. It is git-ignored.
|
||||
- If the Cloudflare MCP or Cloudflare plugin is available, it may be used to inspect or create resources. If not, use Wrangler through the scripts in this repo.
|
||||
- Ask the user only for values that cannot be inferred or generated safely, such as Cloudflare authorization, the first login password, or custom domain ownership.
|
||||
|
||||
## Required Tools
|
||||
|
||||
- Bun
|
||||
- Wrangler, installed by `bun install` as a project dev dependency
|
||||
- A Cloudflare account authorized by either `wrangler login` or `CLOUDFLARE_API_TOKEN`
|
||||
|
||||
## Standard Flow
|
||||
|
||||
1. Clone the repository and enter it.
|
||||
|
||||
```sh
|
||||
git clone <repo-url>
|
||||
cd edgeever
|
||||
```
|
||||
|
||||
2. Install dependencies.
|
||||
|
||||
```sh
|
||||
bun install
|
||||
```
|
||||
|
||||
3. Ensure Cloudflare authentication.
|
||||
|
||||
```sh
|
||||
bunx wrangler whoami
|
||||
```
|
||||
|
||||
If this fails, ask the user to finish Cloudflare login or provide a suitable API token. Do not continue deployment until this works.
|
||||
|
||||
4. Prepare deployment resources and `.env.local`.
|
||||
|
||||
If the user gave a first login password, run:
|
||||
|
||||
```sh
|
||||
EDGE_EVER_PASSWORD='<first-login-password>' bun run deploy:setup
|
||||
```
|
||||
|
||||
If no password was provided, ask for one or ask permission to generate a random password. Then rerun the command with `EDGE_EVER_PASSWORD`.
|
||||
|
||||
`deploy:setup` will:
|
||||
|
||||
- copy `.env.local.example` to `.env.local` when needed
|
||||
- reuse or create the D1 database
|
||||
- create the R2 buckets when needed
|
||||
- generate `EDGE_EVER_AUTH_PASSWORD_HASH` from `EDGE_EVER_PASSWORD`
|
||||
|
||||
5. Check the deployment inputs.
|
||||
|
||||
```sh
|
||||
bun run deploy:doctor
|
||||
```
|
||||
|
||||
Fix every `fail` result before deploying.
|
||||
|
||||
6. Deploy.
|
||||
|
||||
```sh
|
||||
bun run deploy
|
||||
```
|
||||
|
||||
`bun run deploy` builds the web app, applies remote D1 migrations, and deploys the Worker. During deploy, `scripts/run-wrangler.mjs` uploads `EDGE_EVER_AUTH_PASSWORD_HASH` as a Worker secret via a generated `.env.wrangler.generated*.secrets` file.
|
||||
|
||||
7. Verify the result.
|
||||
|
||||
Use the Worker URL from Wrangler output, then check:
|
||||
|
||||
```sh
|
||||
curl -I https://<worker-url>/
|
||||
curl https://<worker-url>/api/openapi.json
|
||||
```
|
||||
|
||||
Open the site, log in with `EDGE_EVER_AUTH_USERNAME` and the first login password, then create an MCP token from the in-app MCP settings.
|
||||
|
||||
## Optional Customization
|
||||
|
||||
Set these values in `.env.local` before `bun run deploy:setup` or `bun run deploy`:
|
||||
|
||||
```sh
|
||||
EDGE_EVER_WORKER_NAME=edgeever
|
||||
EDGE_EVER_D1_DATABASE_NAME=edgeever
|
||||
EDGE_EVER_R2_BUCKET_NAME=edgeever-resources
|
||||
EDGE_EVER_R2_PREVIEW_BUCKET_NAME=edgeever-resources-preview
|
||||
EDGE_EVER_AUTH_USERNAME=admin
|
||||
EDGE_EVER_CUSTOM_DOMAIN=notes.example.com
|
||||
```
|
||||
|
||||
For multiple instances, set `EDGE_EVER_INSTANCE=<name>` and use scoped variables such as:
|
||||
|
||||
```sh
|
||||
EDGE_EVER_PROD_WORKER_NAME=edgeever-prod
|
||||
EDGE_EVER_PROD_D1_DATABASE_ID=<database-id>
|
||||
EDGE_EVER_PROD_R2_BUCKET_NAME=edgeever-prod-resources
|
||||
EDGE_EVER_PROD_AUTH_PASSWORD_HASH=<password-hash>
|
||||
```
|
||||
|
||||
## Blocking Conditions
|
||||
|
||||
Stop and ask the user only when:
|
||||
|
||||
- Cloudflare authentication is missing and the agent cannot open or complete login.
|
||||
- The user must choose or provide the first login password.
|
||||
- The requested custom domain is not available in the Cloudflare account.
|
||||
- Resource creation fails because of account limits, permissions, billing, or conflicting names that cannot be resolved by choosing a new name.
|
||||
|
||||
## Final User Response
|
||||
|
||||
After deployment, report:
|
||||
|
||||
- deployed URL
|
||||
- login username
|
||||
- whether the password was user-provided or generated
|
||||
- where to create the EdgeEver MCP token in the app
|
||||
- any custom domain or Cloudflare DNS step that remains
|
||||
@@ -23,6 +23,8 @@
|
||||
"cli": "bun scripts/edgeever.mjs",
|
||||
"import:evernote": "bun scripts/import-evernote-enex-via-mcp.mjs",
|
||||
"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 build && bun run db:migrate:remote && bun scripts/run-wrangler.mjs deploy"
|
||||
},
|
||||
"cloudflare": {
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const PLACEHOLDER_D1_ID = "00000000-0000-0000-0000-000000000000";
|
||||
const UUID_PATTERN =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
const PASSWORD_HASH_PATTERN = /^pbkdf2-sha256\$100000\$[^$]+\$[^$]+$/;
|
||||
|
||||
const command = process.argv[2] ?? "doctor";
|
||||
if (!["doctor", "setup"].includes(command)) {
|
||||
console.error("Usage: bun scripts/cloudflare-deploy.mjs <doctor|setup>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const envPath = resolve(".env.local");
|
||||
const envExamplePath = resolve(".env.local.example");
|
||||
const localWrangler = resolve(
|
||||
"node_modules",
|
||||
".bin",
|
||||
process.platform === "win32" ? "wrangler.cmd" : "wrangler",
|
||||
);
|
||||
const wrangler = existsSync(localWrangler)
|
||||
? localWrangler
|
||||
: process.platform === "win32"
|
||||
? "wrangler.cmd"
|
||||
: "wrangler";
|
||||
|
||||
const parseEnv = (content) => {
|
||||
const values = new Map();
|
||||
|
||||
for (const line of content.split(/\r?\n/)) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const separatorIndex = trimmed.indexOf("=");
|
||||
const key = trimmed.slice(0, separatorIndex).trim();
|
||||
let value = trimmed.slice(separatorIndex + 1).trim();
|
||||
if (
|
||||
(value.startsWith('"') && value.endsWith('"')) ||
|
||||
(value.startsWith("'") && value.endsWith("'"))
|
||||
) {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
values.set(key, value);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
const readEnv = () => {
|
||||
const values = existsSync(envPath)
|
||||
? parseEnv(readFileSync(envPath, "utf8"))
|
||||
: new Map();
|
||||
|
||||
for (const [key, value] of values) {
|
||||
if (process.env[key] === undefined) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
const scopedKey = (name, values) => {
|
||||
const instance = (values.get("EDGE_EVER_INSTANCE") || process.env.EDGE_EVER_INSTANCE || "")
|
||||
.trim()
|
||||
.replace(/[^a-zA-Z0-9]/g, "_")
|
||||
.toUpperCase();
|
||||
return instance ? `EDGE_EVER_${instance}_${name}` : undefined;
|
||||
};
|
||||
|
||||
const envValue = (name, values) => {
|
||||
const scoped = scopedKey(name, values);
|
||||
return (
|
||||
(scoped ? values.get(scoped) || process.env[scoped] : undefined) ||
|
||||
values.get(`EDGE_EVER_${name}`) ||
|
||||
process.env[`EDGE_EVER_${name}`] ||
|
||||
""
|
||||
).trim();
|
||||
};
|
||||
|
||||
const targetKey = (name, values) => scopedKey(name, values) || `EDGE_EVER_${name}`;
|
||||
|
||||
const upsertEnv = (key, value) => {
|
||||
const content = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
||||
const pattern = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=.*$`, "m");
|
||||
const next = pattern.test(content)
|
||||
? content.replace(pattern, `${key}=${value}`)
|
||||
: `${content.trimEnd()}\n${key}=${value}\n`;
|
||||
|
||||
writeFileSync(envPath, next.startsWith("\n") ? next.slice(1) : next);
|
||||
process.env[key] = value;
|
||||
};
|
||||
|
||||
const run = (executable, args, options = {}) =>
|
||||
spawnSync(executable, args, {
|
||||
cwd: resolve("."),
|
||||
encoding: "utf8",
|
||||
env: process.env,
|
||||
shell: process.platform === "win32",
|
||||
...options,
|
||||
});
|
||||
|
||||
const runWrangler = (args) => run(wrangler, args);
|
||||
|
||||
const check = (label, passed, detail = "") => {
|
||||
const status = passed ? "ok" : "fail";
|
||||
console.log(`[${status}] ${label}${detail ? `: ${detail}` : ""}`);
|
||||
return passed;
|
||||
};
|
||||
|
||||
const ensureEnvLocal = () => {
|
||||
if (existsSync(envPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyFileSync(envExamplePath, envPath);
|
||||
console.log("[ok] created .env.local from .env.local.example");
|
||||
};
|
||||
|
||||
const extractUuid = (text) => {
|
||||
const assignment = text.match(/database_id\s*=\s*"([^"]+)"/);
|
||||
if (assignment?.[1] && UUID_PATTERN.test(assignment[1])) {
|
||||
return assignment[1];
|
||||
}
|
||||
|
||||
const match = text.match(UUID_PATTERN);
|
||||
return match?.[0] ?? "";
|
||||
};
|
||||
|
||||
const findD1DatabaseId = (databaseName) => {
|
||||
const result = runWrangler(["d1", "list", "--json"]);
|
||||
if (result.status !== 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
const databases = JSON.parse(result.stdout);
|
||||
const database = Array.isArray(databases)
|
||||
? databases.find((item) => item?.name === databaseName)
|
||||
: undefined;
|
||||
return database?.uuid || database?.id || "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const ensureD1 = (values) => {
|
||||
const currentId = envValue("D1_DATABASE_ID", values);
|
||||
if (currentId && currentId !== PLACEHOLDER_D1_ID) {
|
||||
return check("D1 database id", UUID_PATTERN.test(currentId), currentId);
|
||||
}
|
||||
|
||||
const databaseName = envValue("D1_DATABASE_NAME", values) || "edgeever";
|
||||
const existingId = findD1DatabaseId(databaseName);
|
||||
if (existingId) {
|
||||
upsertEnv(targetKey("D1_DATABASE_ID", values), existingId);
|
||||
console.log(`[ok] reused D1 database ${databaseName}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = runWrangler(["d1", "create", databaseName]);
|
||||
const output = `${result.stdout}\n${result.stderr}`;
|
||||
if (result.status !== 0) {
|
||||
console.error(output.trim());
|
||||
return check("create D1 database", false, `set ${targetKey("D1_DATABASE_ID", values)} manually`);
|
||||
}
|
||||
|
||||
const databaseId = extractUuid(output);
|
||||
if (!databaseId) {
|
||||
console.error(output.trim());
|
||||
return check("read D1 database id", false, "could not parse wrangler output");
|
||||
}
|
||||
|
||||
upsertEnv(targetKey("D1_DATABASE_ID", values), databaseId);
|
||||
console.log(`[ok] created D1 database ${databaseName}`);
|
||||
return true;
|
||||
};
|
||||
|
||||
const ensureR2 = (values, name) => {
|
||||
const bucketName = envValue(name, values);
|
||||
if (!bucketName) {
|
||||
return name === "R2_PREVIEW_BUCKET_NAME" || check(`${name}`, false, "missing bucket name");
|
||||
}
|
||||
|
||||
const result = runWrangler(["r2", "bucket", "create", bucketName]);
|
||||
const output = `${result.stdout}\n${result.stderr}`.trim();
|
||||
if (result.status === 0 || /already exists|binding already exists|bucket.+exists/i.test(output)) {
|
||||
console.log(`[ok] R2 bucket ${bucketName}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error(output);
|
||||
return check(`create R2 bucket ${bucketName}`, false);
|
||||
};
|
||||
|
||||
const ensurePasswordHash = (values) => {
|
||||
const currentHash = envValue("AUTH_PASSWORD_HASH", values);
|
||||
if (currentHash) {
|
||||
return check("auth password hash", PASSWORD_HASH_PATTERN.test(currentHash), "configured");
|
||||
}
|
||||
|
||||
const password = process.env.EDGE_EVER_PASSWORD?.trim();
|
||||
if (!password) {
|
||||
return check(
|
||||
"auth password hash",
|
||||
false,
|
||||
"set EDGE_EVER_PASSWORD and rerun setup, or set EDGE_EVER_AUTH_PASSWORD_HASH",
|
||||
);
|
||||
}
|
||||
|
||||
const result = run("bun", ["scripts/hash-password.mjs"], {
|
||||
env: { ...process.env, EDGE_EVER_PASSWORD: password },
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
console.error(result.stderr.trim());
|
||||
return check("generate auth password hash", false);
|
||||
}
|
||||
|
||||
const passwordHash = result.stdout.trim();
|
||||
upsertEnv(targetKey("AUTH_PASSWORD_HASH", values), passwordHash);
|
||||
console.log("[ok] generated auth password hash");
|
||||
return true;
|
||||
};
|
||||
|
||||
const doctor = () => {
|
||||
const values = readEnv();
|
||||
let passed = true;
|
||||
|
||||
passed = check("Bun", run("bun", ["--version"]).status === 0) && passed;
|
||||
passed = check("Wrangler", runWrangler(["--version"]).status === 0) && passed;
|
||||
passed = check(".env.local", existsSync(envPath), existsSync(envPath) ? "present" : "missing") && passed;
|
||||
|
||||
const whoami = runWrangler(["whoami"]);
|
||||
passed = check("Cloudflare auth", whoami.status === 0, whoami.status === 0 ? "authenticated" : "run wrangler login") && passed;
|
||||
|
||||
const databaseId = envValue("D1_DATABASE_ID", values);
|
||||
passed =
|
||||
check(
|
||||
"D1 database id",
|
||||
Boolean(databaseId && databaseId !== PLACEHOLDER_D1_ID && UUID_PATTERN.test(databaseId)),
|
||||
databaseId && databaseId !== PLACEHOLDER_D1_ID ? databaseId : "missing",
|
||||
) && passed;
|
||||
|
||||
passed =
|
||||
check("R2 bucket name", Boolean(envValue("R2_BUCKET_NAME", values)), envValue("R2_BUCKET_NAME", values) || "missing") &&
|
||||
passed;
|
||||
|
||||
const passwordHash = envValue("AUTH_PASSWORD_HASH", values);
|
||||
passed =
|
||||
check(
|
||||
"auth password hash",
|
||||
Boolean(passwordHash && PASSWORD_HASH_PATTERN.test(passwordHash)),
|
||||
passwordHash ? "configured" : "missing",
|
||||
) && passed;
|
||||
|
||||
process.exit(passed ? 0 : 1);
|
||||
};
|
||||
|
||||
const setup = () => {
|
||||
ensureEnvLocal();
|
||||
const values = readEnv();
|
||||
let passed = true;
|
||||
|
||||
passed = check("Wrangler", runWrangler(["--version"]).status === 0) && passed;
|
||||
const whoami = runWrangler(["whoami"]);
|
||||
passed = check("Cloudflare auth", whoami.status === 0, whoami.status === 0 ? "authenticated" : "run wrangler login") && passed;
|
||||
|
||||
if (!passed) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
passed = ensureD1(values) && passed;
|
||||
passed = ensureR2(values, "R2_BUCKET_NAME") && passed;
|
||||
passed = ensureR2(values, "R2_PREVIEW_BUCKET_NAME") && passed;
|
||||
passed = ensurePasswordHash(values) && passed;
|
||||
|
||||
process.exit(passed ? 0 : 1);
|
||||
};
|
||||
|
||||
if (command === "setup") {
|
||||
setup();
|
||||
} else {
|
||||
doctor();
|
||||
}
|
||||
@@ -51,6 +51,11 @@ const generatedConfigPath = resolve(
|
||||
? `.wrangler.generated.${instanceKey.toLowerCase()}.toml`
|
||||
: ".wrangler.generated.toml",
|
||||
);
|
||||
const generatedSecretsPath = resolve(
|
||||
instanceKey
|
||||
? `.env.wrangler.generated.${instanceKey.toLowerCase()}.secrets`
|
||||
: ".env.wrangler.generated.secrets",
|
||||
);
|
||||
let config = readFileSync(baseConfigPath, "utf8");
|
||||
let changed = false;
|
||||
|
||||
@@ -164,6 +169,16 @@ if (changed) {
|
||||
writeFileSync(generatedConfigPath, config);
|
||||
}
|
||||
|
||||
const isDeployCommand = wranglerArgs.includes("deploy");
|
||||
const hasSecretsFileArg = wranglerArgs.some((arg) => arg === "--secrets-file" || arg.startsWith("--secrets-file="));
|
||||
const authPasswordHash = envValue("AUTH_PASSWORD_HASH");
|
||||
const finalWranglerArgs = [...wranglerArgs];
|
||||
|
||||
if (isDeployCommand && authPasswordHash && !hasSecretsFileArg) {
|
||||
writeFileSync(generatedSecretsPath, `EDGE_EVER_AUTH_PASSWORD_HASH=${authPasswordHash}\n`);
|
||||
finalWranglerArgs.push("--secrets-file", generatedSecretsPath);
|
||||
}
|
||||
|
||||
const localWrangler = resolve(
|
||||
"node_modules",
|
||||
".bin",
|
||||
@@ -174,7 +189,7 @@ const executable = existsSync(localWrangler)
|
||||
: process.platform === "win32"
|
||||
? "wrangler.cmd"
|
||||
: "wrangler";
|
||||
const result = spawnSync(executable, ["--config", configPath, ...wranglerArgs], {
|
||||
const result = spawnSync(executable, ["--config", configPath, ...finalWranglerArgs], {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user