refactor: v2.2.0 移除本地 sim 模式,Cloudflare OAuth 只走真实授权
- 删除 SimCFApi / WMAILHUB_SIM_CF / isRealOAuth / POST /api/oauth/cloudflare/sim/approve - 删除前端 OAuthSim 模拟授权页与 /oauth/sim/authorize 路由;账号页「添加账号」整页跳 Cloudflare - 迁移 0005 删除 cf_sim_resources 表 - 未配置 CF_OAUTH_CLIENT_ID/SECRET 时 start/callback 一律 503(不再降级出假账号假资源) - 修 OAuth 回调端点:跨站重定向不携带 SameSite=Strict 会话 cookie,改为由一次性 state 承担 CSRF 防护 - 测试改用 tests/cf-mock.ts 在 fetch 层拦截 Cloudflare API,断言真实 HTTP 调用(72/72 通过) - 本地 dev 无账号时只拉起 Console 并提示人工授权,不再自动造账号
This commit is contained in:
+48
-52
@@ -6,9 +6,9 @@
|
||||
* 流程:
|
||||
* 1. 构建前端(vite build)
|
||||
* 2. 启动 Console(:18787,自动迁移 + admin/admin 种子)
|
||||
* 3. 确保存在账号(无则 OAuth sim 自动创建账号),并逐域名配置 Email Workers
|
||||
* (域名 setup 会部署账号级 Email Worker + 签发 WorkerCredential)
|
||||
* 4. 启动 Email Worker(:18789,authorize/ingest 直连 Console)
|
||||
* 3. 已有 OAuth 接入的账号则为其配置未激活域名的 Email Workers
|
||||
* (域名 setup 会部署账号级 Email Worker + 签发 WorkerCredential),然后启动本地 Email Worker
|
||||
* 4. 没有账号时只跑 Console:Cloudflare OAuth 需要浏览器人工授权,不再有模拟通道
|
||||
* 5. Ctrl-C 一起退出
|
||||
*/
|
||||
|
||||
@@ -78,62 +78,58 @@ if (!loginResp.ok) throw new Error("admin 登录失败");
|
||||
const cookie = (loginResp.headers.get("set-cookie") ?? "").split(";")[0];
|
||||
const authHeaders = { Cookie: cookie, "Content-Type": "application/json" };
|
||||
|
||||
// 3. 确保账号存在(无则走 OAuth 自动创建)
|
||||
// 3. 账号:OAuth 授权必须由浏览器人工完成,这里只消费已接入的账号
|
||||
const accountsResp = await fetch(`${BASE}/api/accounts`, { headers: authHeaders });
|
||||
const existing = ((await accountsResp.json()) as { items: { id: string; name: string }[] }).items;
|
||||
let accountId: string;
|
||||
|
||||
if (existing.length > 0) {
|
||||
accountId = existing[0].id;
|
||||
console.log(`[dev] 复用既有账号 ${accountId}`);
|
||||
if (existing.length === 0) {
|
||||
console.log("");
|
||||
console.log("======== WmailHub 本地 Console 已就绪(尚未接入 Cloudflare 账号)========");
|
||||
console.log(` Console: ${BASE} (admin / admin)`);
|
||||
console.log(` 下一步 : 打开 Console → 账号 → 添加账号,完成 Cloudflare OAuth 授权`);
|
||||
console.log(` (需先配置 CF_OAUTH_CLIENT_ID / CF_OAUTH_CLIENT_SECRET,并在 Cloudflare`);
|
||||
console.log(` 登记回调 ${BASE}/api/oauth/cloudflare/callback)`);
|
||||
console.log(" 接入成功后重新执行 `bun run dev` 即可一并拉起本地 Email Worker");
|
||||
console.log("================================================================");
|
||||
console.log("");
|
||||
} else {
|
||||
// 添加账号:OAuth start 自动创建账号,授权后自动接入
|
||||
const startResp = await fetch(`${BASE}/api/oauth/cloudflare/start`, { headers: authHeaders });
|
||||
const { state } = (await startResp.json()) as { state: string };
|
||||
const approve = await fetch(`${BASE}/api/oauth/cloudflare/sim/approve`, {
|
||||
method: "POST",
|
||||
headers: authHeaders,
|
||||
body: JSON.stringify({ state }),
|
||||
const accountId = existing[0].id;
|
||||
console.log(`[dev] 使用已接入账号 ${accountId}`);
|
||||
|
||||
// 4. 域名同步后逐域名配置 Email Workers(部署 Worker + Routing + catch-all;幂等)
|
||||
const domainsResp = await fetch(`${BASE}/api/domains`, { headers: authHeaders });
|
||||
const domains = ((await domainsResp.json()) as { items: { id: string; name: string; account_id: string | null; status: string }[] }).items;
|
||||
for (const d of domains.filter((x) => x.account_id === accountId && x.status !== "active")) {
|
||||
const setup = (await (
|
||||
await fetch(`${BASE}/api/domains/${d.id}/setup`, { method: "POST", headers: authHeaders })
|
||||
).json()) as { ok: boolean; error?: string };
|
||||
if (!setup.ok) throw new Error(`域名 ${d.name} 配置 Email Workers 失败:${setup.error}`);
|
||||
console.log(`[dev] 域名 ${d.name} Email Workers 已配置`);
|
||||
}
|
||||
|
||||
// 5. Email Worker(authorize/ingest 直连 Console;凭据来自 WorkerCredential)
|
||||
const secretResp = (await (
|
||||
await fetch(`${BASE}/api/accounts/${accountId}/worker-secret`, { headers: authHeaders })
|
||||
).json()) as { worker_id: string; secret: string };
|
||||
spawn("worker", ["worker/src/local.ts"], ROOT, {
|
||||
CONSOLE_URL: BASE,
|
||||
WORKER_ID: secretResp.worker_id,
|
||||
WORKER_SECRET: secretResp.secret,
|
||||
WMAILHUB_WORKER_PORT: String(WORKER_PORT),
|
||||
});
|
||||
const approveRes = (await approve.json()) as { ok: boolean; error?: string; account_id: string };
|
||||
if (!approveRes.ok) throw new Error(`自动创建账号失败:${approveRes.error}`);
|
||||
accountId = approveRes.account_id;
|
||||
console.log(`[dev] OAuth(sim)已创建账号 ${accountId} 并同步域名`);
|
||||
await waitReady(`http://127.0.0.1:${WORKER_PORT}/__health`, "email worker");
|
||||
console.log("[dev] email worker ready");
|
||||
|
||||
console.log("");
|
||||
console.log("======== WmailHub 本地环境已就绪 ========");
|
||||
console.log(` Console : ${BASE} (admin / admin)`);
|
||||
console.log(` Email Worker: http://127.0.0.1:${WORKER_PORT}/__health (worker_id=${secretResp.worker_id})`);
|
||||
console.log(" 发测试邮件 : curl -X POST http://127.0.0.1:18789/__email_test \\");
|
||||
console.log(" -H 'X-WmailHub-Test-Secret: <worker secret>' \\");
|
||||
console.log(" -H 'X-WmailHub-Test-To: hello@example.test' --data-binary @mail.eml");
|
||||
console.log("==========================================");
|
||||
}
|
||||
|
||||
// 4. 域名同步后逐域名配置 Email Workers(部署 Worker + Routing + catch-all;幂等)
|
||||
const domainsResp = await fetch(`${BASE}/api/domains`, { headers: authHeaders });
|
||||
const domains = ((await domainsResp.json()) as { items: { id: string; name: string; account_id: string | null; status: string }[] }).items;
|
||||
for (const d of domains.filter((x) => x.account_id === accountId && x.status !== "active")) {
|
||||
const setup = (await (
|
||||
await fetch(`${BASE}/api/domains/${d.id}/setup`, { method: "POST", headers: authHeaders })
|
||||
).json()) as { ok: boolean; error?: string };
|
||||
if (!setup.ok) throw new Error(`域名 ${d.name} 配置 Email Workers 失败:${setup.error}`);
|
||||
console.log(`[dev] 域名 ${d.name} Email Workers 已配置`);
|
||||
}
|
||||
|
||||
// 5. Email Worker(authorize/ingest 直连 Console;凭据来自 WorkerCredential)
|
||||
const secretResp = (await (
|
||||
await fetch(`${BASE}/api/accounts/${accountId}/worker-secret`, { headers: authHeaders })
|
||||
).json()) as { worker_id: string; secret: string };
|
||||
spawn("worker", ["worker/src/local.ts"], ROOT, {
|
||||
CONSOLE_URL: BASE,
|
||||
WORKER_ID: secretResp.worker_id,
|
||||
WORKER_SECRET: secretResp.secret,
|
||||
WMAILHUB_WORKER_PORT: String(WORKER_PORT),
|
||||
});
|
||||
await waitReady(`http://127.0.0.1:${WORKER_PORT}/__health`, "email worker");
|
||||
console.log("[dev] email worker ready");
|
||||
|
||||
console.log("");
|
||||
console.log("======== WmailHub 本地环境已就绪 ========");
|
||||
console.log(` Console : ${BASE} (admin / admin)`);
|
||||
console.log(` Email Worker: http://127.0.0.1:${WORKER_PORT}/__health (worker_id=${secretResp.worker_id})`);
|
||||
console.log(" 发测试邮件 : curl -X POST http://127.0.0.1:18789/__email_test \\");
|
||||
console.log(" -H 'X-WmailHub-Test-Secret: <worker secret>' \\");
|
||||
console.log(" -H 'X-WmailHub-Test-To: hello@example.test' --data-binary @mail.eml");
|
||||
console.log("==========================================");
|
||||
|
||||
/** 退出时统一收割子进程 */
|
||||
function shutdown() {
|
||||
for (const { name, proc } of children) {
|
||||
|
||||
Reference in New Issue
Block a user