# Install - 需求:.NET SDK 8.x, PostgreSQL - 設定:複製 `.env.example` → `.env` - Migration: - 預設由 API 啟動時自動執行(`Db__AutoMigrate=true`) - 需要關閉時請設定 `Db__AutoMigrate=false` - 手動執行可用 `dotnet run --project src/SendEngine.Installer -- migrate` - Webhook Auth 初始化(不使用 SQL 檔,改用 Installer): - 若僅需先建立 tenant 基本資料: - `dotnet run --project src/SendEngine.Installer -- ensure-tenant --tenant-id [--tenant-name ]` - 使用 Installer 建立 webhook client(`id` 自動隨機產生): - `dotnet run --project src/SendEngine.Installer -- add-webhook-client --tenant-id [--tenant-name ] --client-id --name --scopes ` - 例如:`dotnet run --project src/SendEngine.Installer -- add-webhook-client --tenant-id 11111111-1111-1111-1111-111111111111 --tenant-name "Tenant A" --client-id member-center-webhook --name "Member Center Webhook" --scopes newsletter:events.write` - 若 tenant 不存在,Installer 會先自動建立 `tenants` 基本資料,避免 webhook 出現 `tenant_not_found` - 建立成功後,Member Center webhook header `X-Client-Id` 請帶回傳的 `id` - 若要自動同步到 Member Center `POST /integrations/send-engine/webhook-clients/upsert`(保留原手動流程): - `dotnet run --project src/SendEngine.Installer -- add-webhook-client --tenant-id --client-id --name --scopes --upsert-member-center --mc-base-url --mc-client-id --mc-client-secret --mc-scope newsletter:events.write.global` - 可選參數: - `--mc-token-path`(預設 `/oauth/token`) - `--mc-upsert-path`(預設 `/integrations/send-engine/webhook-clients/upsert`) - `--mc-token-url` / `--mc-upsert-url`(使用完整 URL 時可覆蓋 path 組合) - Webhook 驗證規則為 tenant 綁定:`auth_clients.tenant_id` 必須等於 payload `tenant_id` - 不支援 `X-Client-Id` fallback - 預設拒絕 `tenant_id = NULL` 的通用 client(`Webhook__AllowNullTenantClient=false`) - Member Center 回寫授權(建議): - `MemberCenter__BaseUrl`(建議) - `MemberCenter__DisableSubscriptionPath`(預設 `/subscriptions/disable`) - `MemberCenter__TokenPath`(預設 `/oauth/token`) - `MemberCenter__OneClickUnsubscribeTokensPath`(預設 `/newsletter/one-click-unsubscribe-tokens`) - `MemberCenter__ClientId` - `MemberCenter__ClientSecret` - `MemberCenter__Scope=newsletter:events.write` - `MemberCenter__ApiToken` 僅作暫時 fallback(非首選) - Send Job API 驗證(JWT): - `Jwt__Issuer` - `Jwt__Audience` - 建議(Member Center OIDC/JWKS): - `Jwt__Authority`(例如 `http://member-center`) - 或 `Jwt__MetadataAddress`(例如 `http://member-center/.well-known/openid-configuration`) - 若兩者都未設定,會自動回退使用 `MemberCenter__BaseUrl + /.well-known/openid-configuration` - `Jwt__RequireHttpsMetadata`(本機可設 `false`) - 相容舊模式(不建議):`Jwt__SigningKey`(HS 對稱驗簽) - 本機測試輔助(臨時): - `TestFriendly__Enabled=true` 時: - webhook 收到未知 tenant 會自動建立 tenant - `/webhooks/ses` 不做任何 DB 存取,但會保留 Member Center callback 流程(僅用於測試流程打通) - Dev Sender(Mock 發信): - `DevSender__Enabled=true`:背景 worker 會處理 `pending` send jobs,並將每位收件人的預計發送內容寫入 `events_inbox`(`source=dev_sender`, `event_type=send.preview`) - `DevSender__PollIntervalSeconds`:輪詢間隔秒數(預設 5) - `ESP__Provider=ses` 時,背景 worker 會改用 SES `SendBulkEmail` 發送 - SES 相關參數:`Ses__Region`、`Ses__FromEmail`、`Ses__ConfigurationSet`(可選)、`Ses__TemplateName` - `SendBulkEmail` 會使用 SES 模板名稱: - 先讀 `campaign.template.ses_template_name` - 若未提供則回退 `Ses__TemplateName` - 若設定了 Member Center one-click token endpoint,sender 會在發送前批次呼叫 `/newsletter/one-click-unsubscribe-tokens`,僅發送 `status=issued` 的收件者 - 內容替換合約(Mock 與 SES 共用): - `{{email}}` - `{{unsubscribe_url}}`(可在 `template` JSON 中提供 `unsubscribe_url` 模板,例如 `https://member.example/unsub?email={{email}}`) - `{{tenant_id}}` / `{{list_id}}` / `{{campaign_id}}` / `{{send_job_id}}` - 正式環境建議維持 `false`