跳轉到

題材跨環境發送

管理員可透過 Cortex Management 將已儲存的 canonical 題材設定發送到允許的目標環境。依題材名稱建立或完整覆蓋描述、成員、公司名稱與角色;不改其他題材、不復活已封鎖題材、不傳送 coverage Markdown 或工作流草稿。

設定

所有變數由 config/theme_transfer.py 驗證。未設定環境時,跨環境端點回傳503;未設定接收憑證時,接收端回傳503,來源仍可發送到已設定目標。服務間憑證只存在伺服器,與使用者 JWT 分開。

investment-agent/docker-compose.yml 與 investment-agent/docker-compose.sit.yml 只將這些設定傳入 agent-api。本機/手動啟動時,從啟動 Compose 的 shell 匯出,或放入明確指定的 --env-file(例如 investment-agent/.env);API 程序本身不讀取 dotenv。

SIT 自動部署由 .github/workflows/deploy-sit.yml 的 deploy job 從 GitHub Actions 設定注入,沿用現有部署流程,不新增觸發條件或人工部署步驟:

GitHub Actions 類型 名稱 SIT 接收端範例/預設
Variable THEME_TRANSFER_ENVIRONMENT sit;未設定保持關閉
Secret THEME_TRANSFER_TARGETS_JSON SIT 若不向其他環境發送可留空;內容包含目標憑證
Secret THEME_TRANSFER_RECEIVE_TOKEN 與 DEV 的 SIT 目標憑證相同
Variable THEME_TRANSFER_ALLOWED_SOURCES dev
Variable THEME_TRANSFER_TIMEOUT_SECONDS 留空使用30秒
Variable THEME_TRANSFER_PREVIEW_TTL_SECONDS 留空使用600秒
Variable THEME_TRANSFER_ALLOW_HTTP 留空只允許 HTTPS

未設定的選用 JSON/數字變數即使由 Compose 或 Actions 傳入空字串,也沿用後端預設:無發送目標、逾時30秒、預覽期限600秒。環境代碼不套用預設,避免意外啟用功能。

DEV 範例(憑證為示意值,部署時以秘密管理提供至少 32 字元的隨機值):

THEME_TRANSFER_ENVIRONMENT=dev
THEME_TRANSFER_TARGETS_JSON={"sit":{"label":"SIT","url":"https://sit-api.example.com","token":"<receiver-specific-secret-at-least-32-characters>"}}

SIT 範例:

THEME_TRANSFER_ENVIRONMENT=sit
THEME_TRANSFER_RECEIVE_TOKEN=<same-receiver-specific-secret-at-least-32-characters>
THEME_TRANSFER_ALLOWED_SOURCES=dev

選用 THEME_TRANSFER_TIMEOUT_SECONDS=30(範圍 >0 至120)與 THEME_TRANSFER_PREVIEW_TTL_SECONDS=600(整數1至3600)。HTTPS 為預設;隔離開發或可信內網才可明確設 THEME_TRANSFER_ALLOW_HTTP=true。URL 不接受帳密、query 或 fragment;browser 只傳目標 ID,不可指定網址。每批1–50題、每題1–1000成員。目標若另有 Cloudflare Access,需提供可由來源服務存取的受保護路徑;不可為此關閉現有 Access。

上述題數/成員數與遠端回應5 MiB是工程資源上限,用於限制單次驗證、寫入與記憶體用量,並非業務規則;600秒預覽期限是可調預設,用於縮短過期設定被套用的時間窗口。這些數值集中定義於設定模組。

部署接收端程式後,再設定來源目標與接收端憑證及來源清單。既有 CRUD、匯出/匯入及未配置環境的其他 API 不變。此文件不代表共享 DEV/SIT 已部署或寫入。

HTTP 契約

全部 JSON。Admin 三支端點沿用 require_admin;接收端兩支使用專用 Authorization: Bearer <receive-token>。所有下述欄位必填;null 只用於明列欄位。

接收端請求拒絕未知欄位;來源解析遠端回應時忽略新增欄位(包含巢狀成員),仍驗證必填欄位、狀態值及環境/題材對應。此相容性只涵蓋新增回應欄位,未宣告支援破壞既有請求契約的版本變更。

Method / path Request 200 Response
GET /api/admin/theme-store/transfers/environments 無 body {source_environment:string,targets:[{id:string,label:string}]};無目標為 []
POST /api/admin/theme-store/transfers/preview {target_environment:string,theme_names:string[]} Preview
POST /api/admin/theme-store/transfers/send 上述 body 加 preview_token:string Result
POST /api/internal/theme-store/transfers/preview {source_environment:string,target_environment:string,themes:Snapshot[]} Preview
POST /api/internal/theme-store/transfers/apply 上述 body 加 preview_token:string Result

Snapshot:{theme_name:string,description:string|null,members:Member[]}。Member:{ticker:string,company_name:string|null,role:'upstream'|'midstream'|'downstream'|'related'|null}。ticker 為4–6位數且同題不可重複;題材名不超過200字、不可為空或含路徑分隔符/C0控制字元,不可為 . / ..;同批題材名不可重複。

Preview:{source_environment:string,target_environment:string,preview_token:string,expires_at:string,themes:PreviewItem[]}。expires_at 為 ISO UTC。每個 PreviewItem 包含:

  • theme_name:string、action:'create'|'update'|'unchanged'|'conflict'|'invalid'
  • current_member_count:number|null(不存在為 null)、incoming_member_count:number
  • description_changed:boolean
  • added_members:Member[]、removed_members:Member[]
  • changed_members:[{ticker:string,before:Member,after:Member}]
  • error:string|null(無錯誤為 null)

Result:{source_environment:string,target_environment:string,results:[{theme_name:string,status:'created'|'updated'|'unchanged'|'failed',error:string|null}]}。回應逐題依輸入順序,一題一筆。可部分成功;失敗題有可診斷原因。每題描述與成員在同一 transaction 更新,失敗整題 rollback。實際寫入後設 is_edited=true 防止 coverage ETL 蓋回。同內容的手動題材(source=manual)本來就受保護,首次發送即不寫入、不刷新時間;coverage 題材若尚未設 is_edited=true,仍需更新一次以建立保護。

{"target_environment":"sit","theme_names":["AI 伺服器"]}

上例先送 preview;再以同一選取及回傳 preview_token 送 send。來源內容或目標狀態變更會使預覽失效;重送相同 token 已成功題為 no-op,未成功題可再次嘗試。token 過期需重新預覽。

業務與認證錯誤沿用全域 {error:{code:string,message:string,details:object}}:接收憑證不正確401、來源未允許403、環境不符422、token失效409、未配置503。schema validation 使用 FastAPI422 detail 陣列。來源回報目標認證/連線/格式錯誤502、逾時504;不把目標401當成使用者登入過期。發送逾時、連線中斷或目標5xx時不能推斷未寫入,應保留原預覽重試並查逐題結果;預覽失敗則重新預覽。既有 admin auth 缺少 Authorization 可能回422,保持現有契約。