即時報價權限(realtime_quote)Integration Guide — Frontend
Backend branch: feat/realtime-quote-tier-gate → cortex/dev
生效條件:live PostgreSQL 先套 migration 168_realtime_quote_tier_feature.sql,再部署 API(順序見 §6)
Consumers:
- 會員端 App / Web:使用即時報價、盤中 K、首頁大盤即時、持股即時估值的畫面
- Cortex Management:等級權限頁(
/api/admin/membership-tiers)
1. 欄位改名對照表(Breaking)
無欄位改名。Breaking 的地方是權限(§3)與後台等級權限 PUT 的必填欄位(§2)。
2. 新欄位
| Endpoint | 欄位 | 型別 | 必填 | 語意 |
|---|---|---|---|---|
GET /api/admin/membership-tiers |
items[].features.realtime_quote |
boolean |
套完 migration 168 後每個 tier 都有 | 該 tier 是否可使用 7 個即時端點 |
PUT /api/admin/membership-tiers/{tier_name}/permissions |
features.realtime_quote |
boolean(strict,不接受字串或數字) |
是,缺少回 422 | 同上 |
migration 168 的初始值:admin = true;free、advance、premium(以及其他沒有此 key 的 tier)= false。
Cortex Management 的 PUT 若是「把 GET 拿到的完整 features 原樣送回」(member-management.md 的既定做法),不需改動即相容;若前端寫死 4 個 key 組 payload,存任何 tier 的權限都會 422,必須補上 realtime_quote,並在權限矩陣加一個 Boolean 開關。
Consumer type(TypeScript):
type TierFeatures = {
ai_analysis: boolean;
portfolio_export: boolean;
realtime_quote: boolean; // 新增,必填
max_watchlists: number; // -1 = 無限制
max_watchlist_items: number; // -1 = 無限制
};
3. 行為改變(Status code)
受影響的 7 個端點:
| Endpoint | 類型 |
|---|---|
GET /api/stocks/{ticker}/realtime-quote |
REST |
GET /api/stocks/{ticker}/intraday-ohlc |
REST |
GET /api/market/realtime-quote |
REST |
GET /api/market/intraday-ohlc |
REST |
GET /api/stocks/{ticker}/realtime-quote/stream |
SSE |
GET /api/market/realtime-quote/stream |
SSE |
GET /api/portfolio/realtime-quotes/stream |
SSE |
| 情境 | 修前 | 修後 |
|---|---|---|
admin 會員(members.is_admin = true) |
200 | 200(不變) |
非 admin,所屬 tier 的 realtime_quote 為 false 或缺 |
200 | 403 FEATURE_REQUIRED |
非 admin,所屬 tier 的 realtime_quote 被後台改為 true |
— | 200 |
| 無權限 + 不存在的 ticker | 404 | 403(權限檢查先於 ticker) |
無權限 + 非法 interval(intraday-ohlc) |
422 | 403(權限檢查先於 query 驗證) |
無權限 + tickers 內容錯誤(空值、空項目、超過 100 個、格式錯、非台股、未收錄) |
404 / 422 | 403 |
未帶 tickers 參數(portfolio stream) |
422 | 422(不變,FastAPI 參數驗證先於權限檢查) |
未帶 Authorization header |
422 | 422(不變) |
| token 無效/過期 | 401 | 401(不變) |
| 帳號停權 | 403 MEMBER_SUSPENDED |
403 MEMBER_SUSPENDED(不變) |
403 body(與既有 error envelope 相同):
{
"error": {
"code": "FEATURE_REQUIRED",
"message": "This membership does not include realtime_quote",
"details": {}
}
}
SSE 端點在連線時就回 HTTP 403,不會開始送事件。
4. SSE 事件語意
事件型別不變(quote / quotes / session_closed / token_expired / unauthorized)。unauthorized 多了觸發條件:
- 串流中被取消 admin,或所屬 tier 的
realtime_quote被改為 false → 下一輪輪詢送unauthorized並結束 - 伺服器端讀不到 admin 權限設定(設定錯誤,503 類)也會落在
unauthorized
前端處理:unauthorized 不要自動重連(既有規則),但不可一律當成登出——應改顯示日資料,必要時重新打一次 REST 端點,用 401 / 403 FEATURE_REQUIRED 分辨原因。
5. 前端怎麼判斷能不能用即時功能
以端點實際回應為準:收到 403 FEATURE_REQUIRED 就隱藏或降級即時 UI,改用日資料。
不要用 /api/members/me 或 /api/auth/me 的 tier.features.realtime_quote 判斷:這兩個端點回的是會員所屬 tier 的設定,不含 admin 的保留權限,所以 admin 在那裡會看到 false,但實際可以使用。已開 follow-up #1924 追蹤讓 /me 回傳有效權限,完成前請依 403 判斷。
建議流程:進入含即時資訊的頁面時先打一次對應 REST 端點(例如 /api/market/realtime-quote);200 才開 SSE,403 FEATURE_REQUIRED 就整頁停用即時功能、本次 session 不再重試。
6. 部署順序與遷移 checklist
- [ ] 後端:live PostgreSQL 套 migration 168(SIT 由 deploy-sit 的 pending-migration runner 在重建容器前自動套)
- [ ] 後端:部署 API(本機
cortex-api為--reload,主 repo pull 後生效;不需重啟 scheduler) - [ ] 會員端:即時報價、盤中 K、首頁大盤即時、持股即時估值處理 403
FEATURE_REQUIRED(降級日資料,不顯示錯誤彈窗、不重試) - [ ] 會員端:SSE
unauthorized不視為登出 - [ ] 會員端:不用
/me的features.realtime_quote決定是否顯示即時 UI - [ ] Cortex Management:權限頁 PUT 帶上
realtime_quote(原樣回送 GET 的features),權限矩陣加 Boolean 開關 - [ ] 驗收:admin 帳號 7 個端點正常;非 admin 帳號 REST 回 403、SSE 連線回 403
順序風險:程式先上而 migration 未套 → 所有人(含 admin)即時端點 403;migration 先套而程式未上 → 舊程式的權限頁 PUT 因未知 key 回 422。兩步應緊接著做。
7. 測試來源
- 單元測試:
investment-agent/backend/tests/test_realtime_quote_tier_gate.py - 真實 PostgreSQL:
investment-agent/backend/tests/integration/test_realtime_quote_api_integration.py(非 admin 對 7 端點 403、admin 對 4 個 REST 200、串流中取消 admin 送unauthorized、自建 tier 開關控制非 admin 存取)、tests/integration/test_portfolio_realtime_quote_pg.py(持股串流:無權限先於 tickers 驗證回 403、開啟的 tier 可串流、串流中 tier 被關閉送unauthorized)、tests/integration/test_admin_memberships_realtime_quote_pg.py(admin 以權限 API 開啟 tier 後非 admin 立即可用;PUT 缺realtime_quote回 422 且不寫入)、tests/integration/test_migration_168_realtime_quote_feature_pg.py