fix(cypher): auth primitive 改走 service binding(解 self-hosted CF 1042)
壓測階段 11:self-hosted 帳號 cypher 用 fetch(workers.dev) 打同帳號 auth worker 被 CF 子請求限制回 1042,service account token 換不到 → 表單寫不進 Google Sheets。token/解密鏈本身正常(直打 auth worker 回真 ya29)。 架構演化(richblack 2026-06-06 拍板):用戶產生的是 recipe(KV 資料,不 deploy), primitive 是平台固定基礎設施、用戶不新增 → 解除「auth primitive 禁 service binding」 舊禁令。service binding 是 CF 內部 RPC,繞開同 zone 522 + 同帳號 workers.dev 1042。 - wrangler.toml:加 SVC_AUTH_STATIC_KEY/SERVICE_ACCOUNT/OAUTH2(已部署者;mtls 未部署留註解) - auth-dispatcher.ts:binding 優先 svc.fetch(),無 binding fallback fetch(workers.dev) - types.ts:4 個 optional SVC_AUTH_* - deploy.ts 無需改:stripOfficialOnlyBindings 不碰 services,tier1 auth 先於 tier2 cypher - 已驗證 self-hosted(leo21c)13 邏輯零件 binding 實綁成功,auth binding 走同路 規範同步:rule 02 / 03 / CLAUDE.md / pre-bash-guard 例外。SDD: Phase 7。tsc exit 0。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,10 +20,23 @@
|
|||||||
import type { Bindings } from '../types';
|
import type { Bindings } from '../types';
|
||||||
import { resolveAuthRecipe, resolveRecipe } from '../routes/recipes';
|
import { resolveAuthRecipe, resolveRecipe } from '../routes/recipes';
|
||||||
import { wasmWorkerUrl } from '../lib/component-loader';
|
import { wasmWorkerUrl } from '../lib/component-loader';
|
||||||
|
import type { ServiceBinding } from '../types';
|
||||||
|
|
||||||
/** 對應 Phase 1-4 會部署的 auth primitive Worker */
|
/** 對應 Phase 1-4 會部署的 auth primitive Worker */
|
||||||
const SUPPORTED_PRIMITIVES = new Set(['static_key', 'service_account', 'oauth2']);
|
const SUPPORTED_PRIMITIVES = new Set(['static_key', 'service_account', 'oauth2']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* primitive 名 → service binding key(Phase 7,2026-06-06)。
|
||||||
|
* 比照 component-loader 的邏輯零件:有 binding 走 CF 內部 RPC(繞開同 zone 522 + 同帳號 workers.dev 1042),
|
||||||
|
* 無 binding(如 self-hosted 未綁、或 mtls 未部署)fallback 到 fetch(workers.dev)。
|
||||||
|
*/
|
||||||
|
const AUTH_BINDING_MAP: Record<string, keyof import('../types').Bindings> = {
|
||||||
|
static_key: 'SVC_AUTH_STATIC_KEY',
|
||||||
|
service_account: 'SVC_AUTH_SERVICE_ACCOUNT',
|
||||||
|
oauth2: 'SVC_AUTH_OAUTH2',
|
||||||
|
mtls: 'SVC_AUTH_MTLS',
|
||||||
|
};
|
||||||
|
|
||||||
/** auth primitive 本身的 componentId(避免自引用) */
|
/** auth primitive 本身的 componentId(避免自引用) */
|
||||||
const AUTH_PRIMITIVE_IDS = new Set([
|
const AUTH_PRIMITIVE_IDS = new Set([
|
||||||
'auth_static_key',
|
'auth_static_key',
|
||||||
@@ -62,18 +75,27 @@ export async function tryAuthDispatch(
|
|||||||
if (!recipe) return null;
|
if (!recipe) return null;
|
||||||
if (!SUPPORTED_PRIMITIVES.has(recipe.primitive)) return null;
|
if (!SUPPORTED_PRIMITIVES.has(recipe.primitive)) return null;
|
||||||
|
|
||||||
// 走新路徑:HTTP POST 到對應 auth primitive Worker
|
// 呼叫對應 auth primitive Worker(Phase 7,2026-06-06):
|
||||||
// 走 workers.dev 避開同 zone 死鎖(P0 #9)
|
// binding 優先(CF 內部 RPC,繞開同 zone 522 + 同帳號 workers.dev 子請求 1042,壓測階段 11),
|
||||||
const primitiveUrl = wasmWorkerUrl(`auth_${recipe.primitive}`, env.WORKER_SUBDOMAIN);
|
// 無 binding(self-hosted 未綁 / mtls 未部署)fallback 到 fetch(workers.dev)。比照 component-loader makeLogicRunner。
|
||||||
const res = await fetch(primitiveUrl, {
|
const reqInit = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({ action: 'authenticate', api_key: apiKey, service }),
|
||||||
action: 'authenticate',
|
};
|
||||||
api_key: apiKey,
|
|
||||||
service,
|
const bindingKey = AUTH_BINDING_MAP[recipe.primitive];
|
||||||
}),
|
const svc = bindingKey ? (env[bindingKey] as ServiceBinding | undefined) : undefined;
|
||||||
});
|
|
||||||
|
let res: Response;
|
||||||
|
if (svc) {
|
||||||
|
// service binding:用任意 URL,CF 內部 RPC 直送目標 Worker(不經公網)
|
||||||
|
res = await svc.fetch(new Request('https://auth-primitive/', reqInit));
|
||||||
|
} else {
|
||||||
|
// fallback:公網 workers.dev(自架未綁 binding / 開發環境 / mtls)
|
||||||
|
const primitiveUrl = wasmWorkerUrl(`auth_${recipe.primitive}`, env.WORKER_SUBDOMAIN);
|
||||||
|
res = await fetch(primitiveUrl, reqInit);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const text = await res.text().catch(() => '');
|
const text = await res.text().catch(() => '');
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ export type Bindings = {
|
|||||||
SVC_DATE_OPS: ServiceBinding;
|
SVC_DATE_OPS: ServiceBinding;
|
||||||
SVC_VALIDATE_JSON: ServiceBinding;
|
SVC_VALIDATE_JSON: ServiceBinding;
|
||||||
// SVC_AI_TRANSFORM_* 已移除(Phase 2 刪 ai_transform 零件 + wrangler.toml service binding)
|
// SVC_AI_TRANSFORM_* 已移除(Phase 2 刪 ai_transform 零件 + wrangler.toml service binding)
|
||||||
|
// Auth primitive Service Bindings(Phase 7,2026-06-06):繞開 self-hosted 同帳號 workers.dev 子請求 1042。
|
||||||
|
// optional:auth_mtls 尚未部署(無 binding);無 binding 時 auth-dispatcher fallback 到 fetch(workers.dev)。
|
||||||
|
SVC_AUTH_STATIC_KEY?: ServiceBinding;
|
||||||
|
SVC_AUTH_SERVICE_ACCOUNT?: ServiceBinding;
|
||||||
|
SVC_AUTH_OAUTH2?: ServiceBinding;
|
||||||
|
SVC_AUTH_MTLS?: ServiceBinding;
|
||||||
// KV Context Store:節點 output 透過 KV 傳遞,解決同名欄位衝突
|
// KV Context Store:節點 output 透過 KV 傳遞,解決同名欄位衝突
|
||||||
EXEC_CONTEXT: KVNamespace;
|
EXEC_CONTEXT: KVNamespace;
|
||||||
// Recipe Store:API recipe 定義(key: recipe:{canonical_id} 或 idx:{hash_id})
|
// Recipe Store:API recipe 定義(key: recipe:{canonical_id} 或 idx:{hash_id})
|
||||||
|
|||||||
@@ -91,6 +91,24 @@ service = "arcrun-date-ops"
|
|||||||
binding = "SVC_VALIDATE_JSON"
|
binding = "SVC_VALIDATE_JSON"
|
||||||
service = "arcrun-validate-json"
|
service = "arcrun-validate-json"
|
||||||
|
|
||||||
|
# Auth primitive service bindings(credential-primitives-wasm Phase 7,2026-06-06)
|
||||||
|
# 為何:auth-dispatcher 原用 fetch(workers.dev) 打同帳號 auth worker,self-hosted 帳號踩 CF 1042
|
||||||
|
# (壓測階段 11)。service binding 是 CF 內部 RPC,繞開同 zone 522 + 同帳號 1042。
|
||||||
|
# 範圍:只綁「已部署」的 auth worker。auth_mtls 尚未部署(.component-builds 無、官方 404),
|
||||||
|
# 綁不存在的 worker 會讓 deploy 報 "referenced Worker not found"(見上 ai_transform 教訓),
|
||||||
|
# 故 mtls 待它部署後再加。auth-dispatcher 對無 binding 的 primitive 自動 fallback fetch。
|
||||||
|
[[services]]
|
||||||
|
binding = "SVC_AUTH_STATIC_KEY"
|
||||||
|
service = "arcrun-auth-static-key"
|
||||||
|
|
||||||
|
[[services]]
|
||||||
|
binding = "SVC_AUTH_SERVICE_ACCOUNT"
|
||||||
|
service = "arcrun-auth-service-account"
|
||||||
|
|
||||||
|
[[services]]
|
||||||
|
binding = "SVC_AUTH_OAUTH2"
|
||||||
|
service = "arcrun-auth-oauth2"
|
||||||
|
|
||||||
# ai_transform_compile / ai_transform_run 已於 Phase 2(2026-05-29)刪除
|
# ai_transform_compile / ai_transform_run 已於 Phase 2(2026-05-29)刪除
|
||||||
# (Arcrun 是 AI 呼叫的工具,工作流不該內嵌 AI 節點)。對應 worker 已 wrangler delete,
|
# (Arcrun 是 AI 呼叫的工具,工作流不該內嵌 AI 節點)。對應 worker 已 wrangler delete,
|
||||||
# service binding 一併移除(否則 deploy 報 referenced Worker not found)。
|
# service binding 一併移除(否則 deploy 報 referenced Worker not found)。
|
||||||
|
|||||||
Reference in New Issue
Block a user