diff --git a/cloudflare-worker/src/backends.js b/cloudflare-worker/src/backends.js index acb6db7..1a31546 100644 --- a/cloudflare-worker/src/backends.js +++ b/cloudflare-worker/src/backends.js @@ -17,6 +17,8 @@ export const SAFE_ROUTES = Object.freeze([ "/api/admin/session/revoke", "/webhook/tg", "/push/jmap", + "/api/push/register", + "/api/push/disable", "/reconcile", "/ready", ]); diff --git a/cloudflare-worker/src/index.js b/cloudflare-worker/src/index.js index 9308c61..1f55e47 100644 --- a/cloudflare-worker/src/index.js +++ b/cloudflare-worker/src/index.js @@ -16,7 +16,7 @@ * - GET|PUT /api/config、PUT /api/business-config、POST /api/admin/session[/revoke] * → 透传至后端;后端校验 bootstrap 凭据或短期 admin session * - GET /healthz → LB 聚合健康(MOD-HEALTH-AGG) - * - POST /webhook/tg|/push/jmap|/reconcile、GET /ready → 透传 + 有界故障转移 + * - POST /webhook/tg|/push/jmap|/api/push/register|/api/push/disable|/reconcile、GET /ready → 透传 + 有界故障转移 * - 其它 → 404 / 405 * * 日志红线:仅打「方法 / 路径 / origin / 失败类别」,绝不打印 header/body/secret。 @@ -48,6 +48,8 @@ const ROUTE_METHODS = Object.freeze({ "/api/admin/session/revoke": ["POST"], "/webhook/tg": ["POST"], "/push/jmap": ["POST"], + "/api/push/register": ["POST"], + "/api/push/disable": ["POST"], "/reconcile": ["POST"], "/ready": ["GET"], }); diff --git a/cloudflare-worker/test/backends.test.js b/cloudflare-worker/test/backends.test.js index 00fa866..af52ca8 100644 --- a/cloudflare-worker/test/backends.test.js +++ b/cloudflare-worker/test/backends.test.js @@ -45,6 +45,7 @@ test("BACKEND_ORIGINS_JSON parses, dedups, validates all entries", () => { test("route safelist excludes /healthz and unknown paths (C-LB-SINGLE-REG-URL)", () => { for (const r of [ "/", "/assets/config.js", "/assets/styles.css", "/api/status", "/ready", "/webhook/tg", "/push/jmap", "/reconcile", + "/api/push/register", "/api/push/disable", "/api/config", "/api/business-config", "/api/admin/session", "/api/admin/session/revoke", ]) { assert.ok(LB_ROUTES.includes(r), `forwarded: ${r}`); diff --git a/cloudflare-worker/test/index.test.js b/cloudflare-worker/test/index.test.js index 1870bf4..d229097 100644 --- a/cloudflare-worker/test/index.test.js +++ b/cloudflare-worker/test/index.test.js @@ -131,3 +131,28 @@ test("Worker: valid env, GET /ready forwarded to backend with auth header intact restore(); } }); + +test("Worker: POST /api/push/register and /api/push/disable are forwarded", async () => { + const seen = []; + const restore = stubFetch(async (url, init) => { + seen.push([url, init.method]); + return new Response("{\"error\":\"invalid_request\",\"request_id\":\"x\"}", { + status: 400, + headers: { "content-type": "application/json" }, + }); + }); + try { + for (const path of ["/api/push/register", "/api/push/disable"]) { + const res = await handleFetch( + new Request(`https://lb.example${path}`, { method: "POST", body: "{}" }), + makeEnv(), + ); + assert.equal(res.status, 400); + assert.deepEqual(await res.json(), { error: "invalid_request", request_id: "x" }); + } + assert.equal(seen.length, 2); + assert.ok(seen.every(([url, method]) => method === "POST")); + } finally { + restore(); + } +});