From d8e69640881bfd0b1167701dd87923119067ab17 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 16 Apr 2026 17:01:42 +0800 Subject: [PATCH] feat: use CF Service Bindings for logic components (no public network) - Add 15 [[services]] bindings in cypher-executor wrangler.toml - component-loader now calls logic Workers via Service Binding (svc.fetch) instead of public URL fetch (which caused 522 timeout within same zone) - Fallback to public URL if binding not available (dev/testing) - Add ServiceBinding type to Bindings Co-Authored-By: Claude Sonnet 4.6 --- cypher-executor/src/lib/component-loader.ts | 66 +++++++++++++-------- cypher-executor/src/types.ts | 21 +++++++ cypher-executor/wrangler.toml | 60 +++++++++++++++++++ 3 files changed, 121 insertions(+), 26 deletions(-) diff --git a/cypher-executor/src/lib/component-loader.ts b/cypher-executor/src/lib/component-loader.ts index 371490d..7cebbf9 100644 --- a/cypher-executor/src/lib/component-loader.ts +++ b/cypher-executor/src/lib/component-loader.ts @@ -21,23 +21,23 @@ import { BUILTIN_COMPONENTS } from './constants'; import type { Bindings, ComponentRunner } from '../types'; -/** 邏輯零件 canonical_id → Worker URL */ -const LOGIC_COMPONENT_URLS: Record = { - if_control: 'https://if-control.arcrun.dev', - switch: 'https://switch.arcrun.dev', - foreach_control: 'https://foreach-control.arcrun.dev', - filter: 'https://filter.arcrun.dev', - merge: 'https://merge.arcrun.dev', - try_catch: 'https://try-catch.arcrun.dev', - wait: 'https://wait.arcrun.dev', - set: 'https://set.arcrun.dev', - array_ops: 'https://array-ops.arcrun.dev', - string_ops: 'https://string-ops.arcrun.dev', - number_ops: 'https://number-ops.arcrun.dev', - date_ops: 'https://date-ops.arcrun.dev', - validate_json: 'https://validate-json.arcrun.dev', - ai_transform_compile:'https://ai-transform-compile.arcrun.dev', - ai_transform_run: 'https://ai-transform-run.arcrun.dev', +/** 邏輯零件 canonical_id → Service Binding key */ +const LOGIC_BINDING_MAP: Record = { + if_control: 'SVC_IF_CONTROL', + switch: 'SVC_SWITCH', + foreach_control: 'SVC_FOREACH_CONTROL', + filter: 'SVC_FILTER', + merge: 'SVC_MERGE', + try_catch: 'SVC_TRY_CATCH', + wait: 'SVC_WAIT', + set: 'SVC_SET', + array_ops: 'SVC_ARRAY_OPS', + string_ops: 'SVC_STRING_OPS', + number_ops: 'SVC_NUMBER_OPS', + date_ops: 'SVC_DATE_OPS', + validate_json: 'SVC_VALIDATE_JSON', + ai_transform_compile: 'SVC_AI_TRANSFORM_COMPILE', + ai_transform_run: 'SVC_AI_TRANSFORM_RUN', }; /** API 零件 canonical_id → recipe(endpoint + 組裝邏輯)*/ @@ -158,21 +158,35 @@ export function createComponentLoader(env: Bindings) { }; } - // 3. 邏輯零件 Worker - const logicUrl = LOGIC_COMPONENT_URLS[componentId]; - if (logicUrl) { + // 3. 邏輯零件 → Service Binding(CF Workers 直接呼叫,不走公網) + const bindingKey = LOGIC_BINDING_MAP[componentId]; + if (bindingKey) { + const svc = env[bindingKey] as import('../types').ServiceBinding | undefined; + if (svc) { + return async (ctx: unknown) => { + const res = await svc.fetch(new Request('https://component/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(ctx), + })); + if (!res.ok) { + const text = await res.text(); + return { success: false, error: `${componentId} 回傳 ${res.status}: ${text.slice(0, 200)}` }; + } + try { return await res.json(); } + catch { return { success: false, error: `${componentId} 回傳非 JSON` }; } + }; + } + // Service Binding 未配置時 fallback 到公網 URL(開發環境) + const fallbackUrl = `https://${componentId.replace(/_/g, '-')}.arcrun.dev`; return async (ctx: unknown) => { - const res = await fetch(logicUrl, { + const res = await fetch(fallbackUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(ctx), }); - if (!res.ok) { - const text = await res.text(); - return { success: false, error: `${componentId} Worker 回傳 ${res.status}: ${text.slice(0, 200)}` }; - } try { return await res.json(); } - catch { return { success: false, error: `${componentId} Worker 回傳非 JSON` }; } + catch { return { success: false, error: `${componentId} fallback 失敗` }; } }; } diff --git a/cypher-executor/src/types.ts b/cypher-executor/src/types.ts index 469245c..93bb235 100644 --- a/cypher-executor/src/types.ts +++ b/cypher-executor/src/types.ts @@ -1,6 +1,27 @@ // arcrun cypher-executor 型別定義 +// Service Binding 型別(CF Workers 直接呼叫,不走公網) +export type ServiceBinding = { + fetch(request: Request): Promise; +}; + export type Bindings = { + // Logic component Service Bindings + SVC_IF_CONTROL: ServiceBinding; + SVC_SWITCH: ServiceBinding; + SVC_FOREACH_CONTROL: ServiceBinding; + SVC_FILTER: ServiceBinding; + SVC_MERGE: ServiceBinding; + SVC_TRY_CATCH: ServiceBinding; + SVC_WAIT: ServiceBinding; + SVC_SET: ServiceBinding; + SVC_ARRAY_OPS: ServiceBinding; + SVC_STRING_OPS: ServiceBinding; + SVC_NUMBER_OPS: ServiceBinding; + SVC_DATE_OPS: ServiceBinding; + SVC_VALIDATE_JSON: ServiceBinding; + SVC_AI_TRANSFORM_COMPILE: ServiceBinding; + SVC_AI_TRANSFORM_RUN: ServiceBinding; // KV Context Store:節點 output 透過 KV 傳遞,解決同名欄位衝突 EXEC_CONTEXT: KVNamespace; // Webhook Store:key = workflow name,value = Workflow JSON diff --git a/cypher-executor/wrangler.toml b/cypher-executor/wrangler.toml index 86982a0..387aea7 100644 --- a/cypher-executor/wrangler.toml +++ b/cypher-executor/wrangler.toml @@ -26,6 +26,66 @@ bucket_name = "arcrun-wasm" [ai] binding = "AI" +[[services]] +binding = "SVC_IF_CONTROL" +service = "arcrun-if-control" + +[[services]] +binding = "SVC_SWITCH" +service = "arcrun-switch" + +[[services]] +binding = "SVC_FOREACH_CONTROL" +service = "arcrun-foreach-control" + +[[services]] +binding = "SVC_FILTER" +service = "arcrun-filter" + +[[services]] +binding = "SVC_MERGE" +service = "arcrun-merge" + +[[services]] +binding = "SVC_TRY_CATCH" +service = "arcrun-try-catch" + +[[services]] +binding = "SVC_WAIT" +service = "arcrun-wait" + +[[services]] +binding = "SVC_SET" +service = "arcrun-set" + +[[services]] +binding = "SVC_ARRAY_OPS" +service = "arcrun-array-ops" + +[[services]] +binding = "SVC_STRING_OPS" +service = "arcrun-string-ops" + +[[services]] +binding = "SVC_NUMBER_OPS" +service = "arcrun-number-ops" + +[[services]] +binding = "SVC_DATE_OPS" +service = "arcrun-date-ops" + +[[services]] +binding = "SVC_VALIDATE_JSON" +service = "arcrun-validate-json" + +[[services]] +binding = "SVC_AI_TRANSFORM_COMPILE" +service = "arcrun-ai-transform-compile" + +[[services]] +binding = "SVC_AI_TRANSFORM_RUN" +service = "arcrun-ai-transform-run" + [vars] ENVIRONMENT = "production" # MULTI_TENANT = "true"