From 90777e387766e8895489138a0cdef5ecbb5f290d Mon Sep 17 00:00:00 2001 From: uncle6me-web Date: Mon, 29 Jun 2026 12:37:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(auth-recipe):=20=E5=9B=9E=E7=81=8C=20telegr?= =?UTF-8?q?am/line=5Fnotify/kbdb=20=E7=A8=AE=E5=AD=90=EF=BC=8C=E8=A3=9C=20?= =?UTF-8?q?AuthInjectSpec.path=EF=BC=88=E4=BF=AE=20source/live=20=E6=BC=82?= =?UTF-8?q?=E7=A7=BB=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue #13 升級:根因不只 acr parts 殘留,是 auth-recipe-seeds.ts source 漏了 3 個 static_key auth recipe(telegram/line_notify/kbdb),但 prod KV 手動 seed 過 → 任何全新 self-hosted `POST /init/seed` 只 seed 23 個、漏 telegram → self-host(leo21c/mira)telegram_send 的 auth_service:'telegram' resolve 不到 → {{auth.bot_token}} 注入空 → telegram 發訊壞掉。 實測 smoking gun(2026-06-29): - prod cypher.arcrun.dev/auth-recipes = 27 個(含 telegram) - self-host arcrun-cypher-executor.leo21c.workers.dev = 23 個(無 telegram/line_notify/kbdb) 修法(資料/型別,非業務邏輯,守 mindset §1 不建 component): - auth-recipe-seeds.ts 補 telegram(inject.path bot_token)/line_notify(header Bearer)/kbdb(header Bearer) - recipes.ts AuthInjectSpec 補 path?(WASM auth_static_key + SDD §六早有此欄、source 介面漏 → tsc 擋) - google_user(oauth2)暫不回灌:內嵌 client_secret 不可進 git + 介面無 oauth2 欄 → 留 Phase D 形態取自 prod GET /auth-recipes/{service};設計權威 auth-recipe.md §六(telegram path)/§七(kbdb 共用)。 telegram 自此與 notion(header)/gsheets(service_account) 同一條 recipe+auth-recipe 鏈,一致。 tsc 全綠;local 模擬 token-in-path 注入 PASS。 Co-Authored-By: Claude Opus 4.8 (1M context) --- cypher-executor/src/lib/auth-recipe-seeds.ts | 95 ++++++++++++++++++++ cypher-executor/src/routes/recipes.ts | 5 ++ 2 files changed, 100 insertions(+) diff --git a/cypher-executor/src/lib/auth-recipe-seeds.ts b/cypher-executor/src/lib/auth-recipe-seeds.ts index bae1201..ea1ad38 100644 --- a/cypher-executor/src/lib/auth-recipe-seeds.ts +++ b/cypher-executor/src/lib/auth-recipe-seeds.ts @@ -11,6 +11,13 @@ import type { AuthRecipeDefinition } from '../routes/recipes'; const now = Date.now(); +// ⚠️ 已知 source/live drift(2026-06-29 盤點,未在此檔修): +// prod RECIPES KV 另有 `auth_recipe:google_user`(primitive: oauth2)。**故意不回灌 source**,原因: +// (1) 它內嵌 client_secret(GOCSPX-...)= 機密,不可進 git(wiki-secret-scan / 一般安全); +// (2) 本檔的 AuthRecipeDefinition 介面尚無 oauth2 欄位(client_id/secret/token_endpoint/scopes), +// 回灌前需先擴 schema + 把 secret 改成部署期注入(wrangler secret / env),屬獨立工作。 +// → google_user 留待 oauth2 seed 機制(含 secret 注入)獨立處理;本次只修無機密的 static_key 漂移。 + export const AUTH_RECIPE_SEEDS: AuthRecipeDefinition[] = [ // ── Static Key 類 ────────────────────────────────────────────────────────── @@ -556,6 +563,94 @@ export const AUTH_RECIPE_SEEDS: AuthRecipeDefinition[] = [ updated_at: now, }, + // ── 訊息 / URL-path 注入類(static_key)──────────────────────────────────── + // + // 2026-06-29 補:以下三個 static_key auth recipe 一直存在於 prod RECIPES KV(手動 seed 過), + // 但**從未進 source seed**(auth-recipe-seeds.ts)→ 任何全新 self-hosted `POST /init/seed` + // 只會 seed 23 個、漏掉 telegram/line_notify/kbdb → self-host(mira/leo21c)的 telegram 發訊 + // 走不通(telegram_send 的 auth_service:'telegram' 找不到 auth recipe → {{auth.bot_token}} 注入空)。 + // 這正是「source vs live drift = 假綠」(總管反覆踩的同一類)。把 prod 現役定義回灌 source, + // 讓 official 與 self-host 共用同一份種子。形態取自 prod GET /auth-recipes/{service}(2026-06-29)。 + // 設計權威:auth-recipe.md §六(line 70-71, telegram path 注入) + §七(line 150-151, kbdb 共用)。 + + { + kind: 'auth_recipe', + service: 'telegram', + version: 1, + primitive: 'static_key', + base_url: 'https://api.telegram.org', + display_name: 'Telegram Bot', + description: 'Telegram Bot API — sendMessage 等(bot token 注入 URL path /bot{token}/)', + required_secrets: [ + { + key: 'telegram_bot_token', + label: 'Bot Token(從 @BotFather 取得)', + help: '在 Telegram 對 @BotFather 送 /newbot 建立 bot,取得格式為 123456:ABC... 的 token', + help_url: 'https://core.telegram.org/bots/features#botfather', + }, + ], + // path 注入:recipe:telegram_send 的 endpoint 用 {{auth.bot_token}} 從 _auth_path 取值 + // (auth_static_key WASM 解密後輸出 auth_path → auth-dispatcher 帶進 _auth_path + // → makeRecipeRunner interpolate)。token 不落 header/query/body,符合 Telegram 的 URL-path 慣例。 + inject: { + path: { + bot_token: '{{secret.telegram_bot_token}}', + }, + }, + created_at: now, + updated_at: now, + }, + + { + kind: 'auth_recipe', + service: 'line_notify', + version: 1, + primitive: 'static_key', + base_url: 'https://notify-api.line.me', + display_name: 'LINE Notify', + description: 'LINE Notify — 推播訊息(static_key Bearer)', + required_secrets: [ + { + key: 'line_token', + label: 'LINE Notify Token', + help: '至 https://notify-bot.line.me/my/ 發行個人存取權杖', + help_url: 'https://notify-bot.line.me/my/', + }, + ], + inject: { + header: { + Authorization: 'Bearer {{secret.line_token}}', + }, + }, + created_at: now, + updated_at: now, + }, + + { + kind: 'auth_recipe', + service: 'kbdb', + version: 1, + primitive: 'static_key', + base_url: 'https://kbdb.finally.click', + display_name: 'KBDB', + description: 'KBDB partner API — block 讀寫(static_key Bearer)。kbdb_* recipe 共用此把 auth。', + required_secrets: [ + { + key: 'kbdb_api_key', + label: 'KBDB API Key(至 arcrun 取統一 API Key 當 credential)', + help: 'KBDB 採 Supabase 模式:要用 → 去 arcrun 取統一 API Key 當此 credential', + help_url: 'https://arcrun.dev', + }, + ], + inject: { + header: { + Authorization: 'Bearer {{secret.kbdb_api_key}}', + }, + }, + created_at: now, + updated_at: now, + }, + // ── Service Account 類(Google 家族,共用同一份 service_account_json)──────── { diff --git a/cypher-executor/src/routes/recipes.ts b/cypher-executor/src/routes/recipes.ts index af22f9e..76bed0c 100644 --- a/cypher-executor/src/routes/recipes.ts +++ b/cypher-executor/src/routes/recipes.ts @@ -460,6 +460,11 @@ export interface AuthInjectSpec { header?: Record; // e.g. { Authorization: "Bearer {{secret.token}}" } query?: Record; body?: Record; + // path:注入 endpoint URL path 的 secret(auth-recipe.md §六,2026-05-29 加)。 + // 解 telegram 類「token 在 URL path」(/bot{token}/)—— header/query/body 都不適用。 + // key = 模板變數名(recipe endpoint 用 {{auth.K}} 引用),value = {{secret.X}} 模板。 + // auth_static_key WASM 解密後輸出為 auth_path → auth-dispatcher 帶進 _auth_path → makeRecipeRunner interpolate。 + path?: Record; } export interface AuthRecipeDefinition {