/** * 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, }, ];