## 背景 需要新增 API Key 使用量頁面,並同步調整 key lifecycle 與 provider revoke 行為。 本次需求包含四個主題: 1. 新增使用量頁面 `/usage` 2. `expired` key 改走 `renew` 3. 停用 key 時 provider 改呼叫 `/key/delete` 4. `extend` 對 provider 固定送基數天數,不再計算累積總天數 ## 需求摘要 - `renew` 允許 `expired` 與 `revoked` - `extend` 只允許 `active` - 停用 key 時,provider endpoint 從 `/key/block` 改為 `/key/delete` - 本地 key 狀態仍維持 `revoked`,不新增 `deleted` 狀態 - 新增 `/usage` 頁面,`user` 與 `admin` 都可用 - 使用量圖表只做「按日期」查詢,時間範圍可自訂 - 不做小時圖 - `extend` 對 provider 一律直接送 `30|180|360`,不再依 `created_at -> expires_at` 計算總天數 ## 規格變更 ### 1. Key lifecycle - `POST /api/v1/api-keys/{id}/renew` - 來源狀態由原本僅 `revoked` 改為允許 `revoked|expired` - renew 維持既有語意:建立新 key,回傳一次性 plaintext - 舊 key 依既有規則對一般使用者隱藏 - `POST /api/v1/api-keys/{id}/extend` - 改為只允許 `active` - `expired` 呼叫時需回 `KEY_NOT_EXTENDABLE` - 本地資料仍更新: - `application_date` = 本次展延起算日 - `duration_days` = `original_duration_days` - `expires_at` = 依該基數重算 - provider payload 的 `duration` 一律直接送 `original_duration_days` - 移除現有「依 key 建立日累算總天數」邏輯 - `POST /api/v1/api-keys/{id}/revoke` - provider client 從 `/key/block` 改成 `/key/delete` - 本地 DB 仍寫入 `revoked` ### 2. My API Keys Page - `active` 顯示 `extend` - `expired` 顯示 `renew` - `revoked` 顯示 `renew` - `expired` 不再顯示 `extend` ### 3. Usage Page - 新增路由 `/usage` - `user` 與 `admin` 都可見 - 進頁後需先選一把 key - 支援自訂日期區間 - 圖表顯示每日使用量 - 主圖表指標為 `total_tokens` - tooltip 顯示: - `prompt_tokens` - `completion_tokens` - `total_tokens` - `spend` - 提供 Loading / Empty / Error / Retry 狀態 ## 資料模型 沿用並擴充現有 `api_key_usage_snapshots`,不新增第二張 usage 時序表。 ### 新增欄位 - `bucket_granularity`,第一版固定使用 `day` - `bucket_start_utc` - `bucket_end_utc` ### 新增唯一鍵 - `(api_key_id, bucket_granularity, bucket_start_utc)` ### 保留既有欄位 - `spend` - `prompt_tokens` - `completion_tokens` - `total_tokens` - `budget_reset_at` - `synced_at` ### 說明 - `api_key_usage_snapshots` 正式作為每日聚合歷史來源 - `api_keys.usage_*` 快取欄位保留,繼續提供 `My API Keys` 的 usage popover / health 使用 - 不做小時資料模型 - 不做舊資料的精確回填,避免錯誤 bucket 污染圖表語意 ## 使用量同步 - sync 腳本改為以「日」為單位聚合 provider spend logs - 每次同步對 active keys 抓取最近一段日期區間資料,建議先抓最近 30 天 - 同一天 bucket 允許後續 sync 覆寫 - 不做獨立 backfill 腳本 - 新頁面的日圖以功能上線後累積資料為主 ## 新增 API ### `GET /api/v1/api-keys/usage-series` #### 權限 - `user` 只能查自己的 key - `admin` 可查任意 key #### Request - `key_id` 必填 - `granularity=day` 必填 - `from` 必填 - `to` 必填 #### Response - `items[]` - 每筆包含: - `bucket_start` - `bucket_label` - `prompt_tokens` - `completion_tokens` - `total_tokens` - `spend` - 回傳: - `granularity` - `key_id` - `from` - `to` #### 時間語意 - DB 以 UTC 儲存 bucket - 查詢與畫面日期區間以 Asia/Taipei 的日曆日為基準 ## 測試項目 ### lifecycle - `expired` key 可成功 renew - `revoked` key 仍可成功 renew - `expired` key 不可 extend - `active` key 仍可 extend - extend 時 provider payload 的 `duration` 必須直接等於 `original_duration_days` - extend 不可再送累積總天數 - revoke 會呼叫 provider `/key/delete` - provider delete 失敗時,本地狀態不得提前改成 `revoked` ### usage-series API - `user` 查他人 key 會被拒絕 - 可正確回傳日期區間的每日 token 聚合 - 日期區間跨 UTC 換日時,Taipei 日期分組正確 - 無資料時回傳正確空結構 - 日期區間參數非法時回 `422` ### sync job - 同一天 bucket 可重跑覆寫,不重複灌資料 - rolling window 重抓時可更新既有日期 bucket - 非 `active` key 不再同步新資料,但既有歷史資料保留 ### frontend - 主導覽顯示 `/usage` - 使用量頁可載入 key 選單並依日期區間畫圖 - `My API Keys` 的 `expired|revoked` 顯示 renew - `My API Keys` 只有 `active` 顯示 extend ## 驗收標準 - 使用者可在 `/usage` 依 key + 日期區間查看每日 token 使用量 - `expired` key 不再能 extend,但可以 renew - `revoked` key 可 renew - `active` key extend 時,provider 僅收到固定基數 `30|180|360` - 停用 key 時,provider 改呼叫 `/key/delete` - 本地狀態與既有 status contract 仍維持 `active|revoked|expired`
背景
需要新增 API Key 使用量頁面,並同步調整 key lifecycle 與 provider revoke 行為。
本次需求包含四個主題:
/usageexpiredkey 改走renew/key/deleteextend對 provider 固定送基數天數,不再計算累積總天數需求摘要
renew允許expired與revokedextend只允許active/key/block改為/key/deleterevoked,不新增deleted狀態/usage頁面,user與admin都可用extend對 provider 一律直接送30|180|360,不再依created_at -> expires_at計算總天數規格變更
1. Key lifecycle
POST /api/v1/api-keys/{id}/renewrevoked改為允許revoked|expiredPOST /api/v1/api-keys/{id}/extendactiveexpired呼叫時需回KEY_NOT_EXTENDABLEapplication_date= 本次展延起算日duration_days=original_duration_daysexpires_at= 依該基數重算duration一律直接送original_duration_daysPOST /api/v1/api-keys/{id}/revoke/key/block改成/key/deleterevoked2. My API Keys Page
active顯示extendexpired顯示renewrevoked顯示renewexpired不再顯示extend3. Usage Page
/usageuser與admin都可見total_tokensprompt_tokenscompletion_tokenstotal_tokensspend資料模型
沿用並擴充現有
api_key_usage_snapshots,不新增第二張 usage 時序表。新增欄位
bucket_granularity,第一版固定使用daybucket_start_utcbucket_end_utc新增唯一鍵
(api_key_id, bucket_granularity, bucket_start_utc)保留既有欄位
spendprompt_tokenscompletion_tokenstotal_tokensbudget_reset_atsynced_at說明
api_key_usage_snapshots正式作為每日聚合歷史來源api_keys.usage_*快取欄位保留,繼續提供My API Keys的 usage popover / health 使用使用量同步
新增 API
GET /api/v1/api-keys/usage-series權限
user只能查自己的 keyadmin可查任意 keyRequest
key_id必填granularity=day必填from必填to必填Response
items[]bucket_startbucket_labelprompt_tokenscompletion_tokenstotal_tokensspendgranularitykey_idfromto時間語意
測試項目
lifecycle
expiredkey 可成功 renewrevokedkey 仍可成功 renewexpiredkey 不可 extendactivekey 仍可 extendduration必須直接等於original_duration_days/key/deleterevokedusage-series API
user查他人 key 會被拒絕422sync job
activekey 不再同步新資料,但既有歷史資料保留frontend
/usageMy API Keys的expired|revoked顯示 renewMy API Keys只有active顯示 extend驗收標準
/usage依 key + 日期區間查看每日 token 使用量expiredkey 不再能 extend,但可以 renewrevokedkey 可 renewactivekey extend 時,provider 僅收到固定基數30|180|360/key/deleteactive|revoked|expired