90777e3877
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) <noreply@anthropic.com>
749 lines
22 KiB
TypeScript
749 lines
22 KiB
TypeScript
/**
|
||
* Auth Recipe Seeds
|
||
*
|
||
* 平台預建的 auth recipe 定義,部署時寫入 RECIPES KV。
|
||
* 新增服務 = 在此加一筆,不需改其他程式碼。
|
||
*
|
||
* KV key: auth_recipe:{service}
|
||
*/
|
||
|
||
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 類 ──────────────────────────────────────────────────────────
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'notion',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.notion.com/v1',
|
||
display_name: 'Notion',
|
||
description: 'Notion API — 頁面、資料庫讀寫',
|
||
required_secrets: [
|
||
{
|
||
key: 'notion_token',
|
||
label: 'Internal Integration Token',
|
||
help: '至 https://www.notion.so/my-integrations 建立 Integration',
|
||
help_url: 'https://www.notion.so/my-integrations',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.notion_token}}',
|
||
'Notion-Version': '2022-06-28',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'slack',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://slack.com/api',
|
||
display_name: 'Slack',
|
||
description: 'Slack Bot API — 發訊息、查頻道',
|
||
required_secrets: [
|
||
{
|
||
key: 'slack_bot_token',
|
||
label: 'Bot User OAuth Token (xoxb-...)',
|
||
help: '至 https://api.slack.com/apps 建立 App,取得 Bot Token',
|
||
help_url: 'https://api.slack.com/apps',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.slack_bot_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'github',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.github.com',
|
||
display_name: 'GitHub',
|
||
description: 'GitHub REST API — repo、issue、PR 操作',
|
||
required_secrets: [
|
||
{
|
||
key: 'github_token',
|
||
label: 'Personal Access Token (classic 或 fine-grained)',
|
||
help: '至 https://github.com/settings/tokens 建立',
|
||
help_url: 'https://github.com/settings/tokens',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.github_token}}',
|
||
Accept: 'application/vnd.github+json',
|
||
'X-GitHub-Api-Version': '2022-11-28',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'openai',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.openai.com/v1',
|
||
display_name: 'OpenAI',
|
||
description: 'OpenAI API — Chat Completions、Embeddings 等',
|
||
required_secrets: [
|
||
{
|
||
key: 'openai_api_key',
|
||
label: 'API Key (sk-...)',
|
||
help: '至 https://platform.openai.com/api-keys 建立',
|
||
help_url: 'https://platform.openai.com/api-keys',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.openai_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'anthropic',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.anthropic.com/v1',
|
||
display_name: 'Anthropic (Claude)',
|
||
description: 'Anthropic API — Claude 模型呼叫',
|
||
required_secrets: [
|
||
{
|
||
key: 'anthropic_api_key',
|
||
label: 'API Key',
|
||
help: '至 https://console.anthropic.com/settings/keys 建立',
|
||
help_url: 'https://console.anthropic.com/settings/keys',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
'x-api-key': '{{secret.anthropic_api_key}}',
|
||
'anthropic-version': '2023-06-01',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'airtable',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.airtable.com/v0',
|
||
display_name: 'Airtable',
|
||
description: 'Airtable API — 讀寫 Base 資料',
|
||
required_secrets: [
|
||
{
|
||
key: 'airtable_token',
|
||
label: 'Personal Access Token',
|
||
help: '至 https://airtable.com/create/tokens 建立',
|
||
help_url: 'https://airtable.com/create/tokens',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.airtable_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'discord',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://discord.com/api/v10',
|
||
display_name: 'Discord',
|
||
description: 'Discord Bot API — 發訊息、管理伺服器',
|
||
required_secrets: [
|
||
{
|
||
key: 'discord_bot_token',
|
||
label: 'Bot Token',
|
||
help: '至 https://discord.com/developers/applications 建立 Bot,取得 Token',
|
||
help_url: 'https://discord.com/developers/applications',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bot {{secret.discord_bot_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'stripe',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.stripe.com/v1',
|
||
display_name: 'Stripe',
|
||
description: 'Stripe API — 支付、客戶、訂閱管理',
|
||
required_secrets: [
|
||
{
|
||
key: 'stripe_secret_key',
|
||
label: 'Secret Key (sk_live_... 或 sk_test_...)',
|
||
help: '至 https://dashboard.stripe.com/apikeys 取得',
|
||
help_url: 'https://dashboard.stripe.com/apikeys',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.stripe_secret_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'twilio',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.twilio.com/2010-04-01',
|
||
display_name: 'Twilio',
|
||
description: 'Twilio API — SMS、電話、WhatsApp',
|
||
required_secrets: [
|
||
{
|
||
key: 'twilio_account_sid',
|
||
label: 'Account SID',
|
||
help: '至 https://console.twilio.com/ 取得',
|
||
help_url: 'https://console.twilio.com/',
|
||
},
|
||
{
|
||
key: 'twilio_auth_token',
|
||
label: 'Auth Token',
|
||
help: '至 https://console.twilio.com/ 取得',
|
||
help_url: 'https://console.twilio.com/',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Basic {{secret.twilio_account_sid}}:{{secret.twilio_auth_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'sendgrid',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.sendgrid.com/v3',
|
||
display_name: 'SendGrid',
|
||
description: 'SendGrid Email API — 發送交易郵件',
|
||
required_secrets: [
|
||
{
|
||
key: 'sendgrid_api_key',
|
||
label: 'API Key (SG....)',
|
||
help: '至 https://app.sendgrid.com/settings/api_keys 建立',
|
||
help_url: 'https://app.sendgrid.com/settings/api_keys',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.sendgrid_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'hubspot',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.hubapi.com',
|
||
display_name: 'HubSpot',
|
||
description: 'HubSpot CRM API — 聯絡人、公司、交易管理',
|
||
required_secrets: [
|
||
{
|
||
key: 'hubspot_token',
|
||
label: 'Private App Access Token',
|
||
help: '至 HubSpot Settings → Integrations → Private Apps 建立',
|
||
help_url: 'https://developers.hubspot.com/docs/api/private-apps',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.hubspot_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'linear',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.linear.app',
|
||
display_name: 'Linear',
|
||
description: 'Linear API — Issue、Project 管理',
|
||
required_secrets: [
|
||
{
|
||
key: 'linear_api_key',
|
||
label: 'Personal API Key',
|
||
help: '至 https://linear.app/settings/api 建立',
|
||
help_url: 'https://linear.app/settings/api',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: '{{secret.linear_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'shopify',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://{{secret.shopify_store}}.myshopify.com/admin/api/2024-01',
|
||
display_name: 'Shopify',
|
||
description: 'Shopify Admin API — 訂單、商品、客戶管理',
|
||
required_secrets: [
|
||
{
|
||
key: 'shopify_access_token',
|
||
label: 'Admin API Access Token',
|
||
help: '至 Shopify Admin → Apps → App and sales channel settings → Private apps',
|
||
help_url: 'https://shopify.dev/docs/apps/auth/admin-app-access-tokens',
|
||
},
|
||
{
|
||
key: 'shopify_store',
|
||
label: 'Store subdomain(不含 .myshopify.com)',
|
||
help: '例如 my-store(對應 my-store.myshopify.com)',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
'X-Shopify-Access-Token': '{{secret.shopify_access_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'resend',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.resend.com',
|
||
display_name: 'Resend',
|
||
description: 'Resend Email API — 發送交易郵件',
|
||
required_secrets: [
|
||
{
|
||
key: 'resend_api_key',
|
||
label: 'API Key (re_...)',
|
||
help: '至 https://resend.com/api-keys 建立',
|
||
help_url: 'https://resend.com/api-keys',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.resend_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'supabase',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://{{secret.supabase_project_ref}}.supabase.co/rest/v1',
|
||
display_name: 'Supabase',
|
||
description: 'Supabase REST API — 資料庫讀寫',
|
||
required_secrets: [
|
||
{
|
||
key: 'supabase_service_key',
|
||
label: 'Service Role Key (eyJ...)',
|
||
help: '至 Supabase Project Settings → API → service_role key',
|
||
help_url: 'https://supabase.com/dashboard',
|
||
},
|
||
{
|
||
key: 'supabase_project_ref',
|
||
label: 'Project Reference ID(URL 中的 xxx.supabase.co 的 xxx)',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.supabase_service_key}}',
|
||
apikey: '{{secret.supabase_service_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'typeform',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.typeform.com',
|
||
display_name: 'Typeform',
|
||
description: 'Typeform API — 表單、問卷回應讀取',
|
||
required_secrets: [
|
||
{
|
||
key: 'typeform_token',
|
||
label: 'Personal Access Token',
|
||
help: '至 https://admin.typeform.com/account#/section/tokens 建立',
|
||
help_url: 'https://developer.typeform.com/get-started/',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{secret.typeform_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'jira',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://{{secret.jira_domain}}.atlassian.net/rest/api/3',
|
||
display_name: 'Jira',
|
||
description: 'Jira API — Issue、Sprint、Project 管理',
|
||
required_secrets: [
|
||
{
|
||
key: 'jira_api_token',
|
||
label: 'API Token',
|
||
help: '至 https://id.atlassian.com/manage-profile/security/api-tokens 建立',
|
||
help_url: 'https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/',
|
||
},
|
||
{
|
||
key: 'jira_email',
|
||
label: '你的 Atlassian 帳號 Email',
|
||
},
|
||
{
|
||
key: 'jira_domain',
|
||
label: 'Jira 子網域(xxx.atlassian.net 的 xxx)',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Basic {{secret.jira_email}}:{{secret.jira_api_token}}',
|
||
Accept: 'application/json',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'gemini',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://generativelanguage.googleapis.com/v1beta',
|
||
display_name: 'Google Gemini',
|
||
description: 'Google Gemini API — generateContent / embedContent(使用 API Key)',
|
||
required_secrets: [
|
||
{
|
||
key: 'gemini_api_key',
|
||
label: 'API Key',
|
||
help: '至 https://aistudio.google.com/apikey 建立',
|
||
help_url: 'https://aistudio.google.com/apikey',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
'x-goog-api-key': '{{secret.gemini_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'trello',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.trello.com/1',
|
||
display_name: 'Trello',
|
||
description: 'Trello API — boards / cards / lists(API key + token 走 query string)',
|
||
required_secrets: [
|
||
{
|
||
key: 'trello_api_key',
|
||
label: 'API Key',
|
||
help: '至 https://trello.com/power-ups/admin 建立 Power-Up 後取得',
|
||
help_url: 'https://trello.com/power-ups/admin',
|
||
},
|
||
{
|
||
key: 'trello_token',
|
||
label: 'Token',
|
||
help: '於 Power-Up 頁面點「Generate Token」授權後取得',
|
||
help_url: 'https://trello.com/power-ups/admin',
|
||
},
|
||
],
|
||
inject: {
|
||
query: {
|
||
key: '{{secret.trello_api_key}}',
|
||
token: '{{secret.trello_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'mailgun',
|
||
version: 1,
|
||
primitive: 'static_key',
|
||
base_url: 'https://api.mailgun.net/v3',
|
||
display_name: 'Mailgun',
|
||
description: 'Mailgun API — 寄信(username 固定 "api",password 為 Private API Key,走 Basic Auth)',
|
||
required_secrets: [
|
||
{
|
||
key: 'mailgun_api_key',
|
||
label: 'Private API Key',
|
||
help: '至 Mailgun Dashboard → API Security → Sending Keys 建立',
|
||
help_url: 'https://app.mailgun.com/mg/sending/domains',
|
||
},
|
||
{
|
||
key: 'mailgun_domain',
|
||
label: 'Sending Domain',
|
||
help: '你在 Mailgun 設定好的 sending domain(例:mg.yourdomain.com)',
|
||
help_url: 'https://app.mailgun.com/mg/sending/domains',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Basic api:{{secret.mailgun_api_key}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
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)────────
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'google_sheets_sa',
|
||
version: 1,
|
||
primitive: 'service_account',
|
||
service_account_kind: 'google_jwt',
|
||
base_url: 'https://sheets.googleapis.com/v4',
|
||
display_name: 'Google Sheets (Service Account)',
|
||
description: 'Google Sheets API — 試算表讀寫(使用 Service Account)',
|
||
token_exchange: {
|
||
endpoint: 'https://oauth2.googleapis.com/token',
|
||
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
|
||
},
|
||
required_secrets: [
|
||
{
|
||
key: 'google_service_account',
|
||
label: 'Service Account JSON(整份貼上)',
|
||
type: 'json_blob',
|
||
help: '至 GCP Console → IAM → Service Accounts → Keys → Add Key → JSON,下載後整份貼入',
|
||
help_url: 'https://console.cloud.google.com/iam-admin/serviceaccounts',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{runtime.access_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'google_gmail_sa',
|
||
version: 1,
|
||
primitive: 'service_account',
|
||
service_account_kind: 'google_jwt',
|
||
base_url: 'https://gmail.googleapis.com/gmail/v1',
|
||
display_name: 'Gmail (Service Account)',
|
||
description: 'Gmail API — 發送郵件(使用 Service Account + Domain-Wide Delegation)',
|
||
token_exchange: {
|
||
endpoint: 'https://oauth2.googleapis.com/token',
|
||
scopes: ['https://www.googleapis.com/auth/gmail.send'],
|
||
},
|
||
required_secrets: [
|
||
{
|
||
key: 'google_service_account',
|
||
label: 'Service Account JSON(整份貼上)',
|
||
type: 'json_blob',
|
||
help: '需要 Domain-Wide Delegation,至 GCP Console → IAM → Service Accounts 設定',
|
||
help_url: 'https://developers.google.com/workspace/guides/create-credentials#service-account',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{runtime.access_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
|
||
{
|
||
kind: 'auth_recipe',
|
||
service: 'google_drive_sa',
|
||
version: 1,
|
||
primitive: 'service_account',
|
||
service_account_kind: 'google_jwt',
|
||
base_url: 'https://www.googleapis.com/drive/v3',
|
||
display_name: 'Google Drive (Service Account)',
|
||
description: 'Google Drive API — 檔案上傳、下載、管理(使用 Service Account)',
|
||
token_exchange: {
|
||
endpoint: 'https://oauth2.googleapis.com/token',
|
||
scopes: ['https://www.googleapis.com/auth/drive'],
|
||
},
|
||
required_secrets: [
|
||
{
|
||
key: 'google_service_account',
|
||
label: 'Service Account JSON(整份貼上)',
|
||
type: 'json_blob',
|
||
help: '至 GCP Console → IAM → Service Accounts → Keys → Add Key → JSON',
|
||
help_url: 'https://console.cloud.google.com/iam-admin/serviceaccounts',
|
||
},
|
||
],
|
||
inject: {
|
||
header: {
|
||
Authorization: 'Bearer {{runtime.access_token}}',
|
||
},
|
||
},
|
||
created_at: now,
|
||
updated_at: now,
|
||
},
|
||
];
|