- feature-device-mgmt-tdd.md + api-device-mgmt.md(B 設備管理 TDD) - feature-model-sharing-tdd.md + api-model-sharing.md + PRD feature + 設計規格(C 模型共享三方規劃) - adr-020-ffmpeg-camera-indev.md(camera 三平台 indev) - PRD.md / TDD.md 索引增補 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.6 KiB
API Spec — 模型共享(Model Sharing)
對齊
feature-model-sharing-tdd.md。base URL / envelope / 認證同api-spec.md(§0)。 通用回應:{ "success": true, "data": {...} }/{ "success": false, "error": { "code", "message" } }。 認證:cookie session(BFF,同api-spec.md),handler 從UserContext取userID/roles/orgId。狀態:Draft(待三方互審)。標
[需 PM 確認]/[需 Design 確認]/[需 security 審]的點見各節。
0. 名詞與前提
| 名詞 | 定義 |
|---|---|
| visibility | 模型的「公開對象」維度(廣播式):private(僅擁有者)/ tenant(同租戶可見)/ public(全平台可見)。存在 models.visibility 欄。 |
| model_shares | 「點對點分享給特定人」維度(ADR-017 決策 3 已定義,本功能沿用、不重造)。與 visibility 正交。 |
| 共享模型庫 | 「依身份權限的可見模型列表」= 我的 ∪ 公開(public) ∪ 同租戶(tenant 且同 org) ∪ 分享給我(model_shares) ∪ preset。 |
| tenant / org | 沿用既有 users.org_id(migration 0001 已有欄位、domain 尚未使用,見 TDD §2)。tenant visibility = 同 org_id 可見。[需 PM 確認] org 邊界語意。 |
與既有
/api/models的分工:既有GET /api/models(api-spec.md §4)維持「我的模型 + preset」語意不變(owner dashboard 用),不動。共享模型庫是新端點GET /api/models/library(跨擁有者、依權限可見)。兩者刻意分開,避免破壞既有 owner dashboard 行為。[需 Design 確認]UI 是否分兩個入口。
1. GET /api/models/library — 共享模型庫列表(依身份權限可見)
依當前 user 身份,回「可見模型」的分頁列表。可見範圍 = 我的 ∪ public ∪ (tenant 且同 org) ∪ 分享給我 ∪ preset。
Query 參數
| 參數 | 型別 | 預設 | 說明 |
|---|---|---|---|
cursor |
string | — | 分頁游標(不透明 base64)。首頁不帶。見 §1.3 分頁契約。 |
limit |
int | 20 | 每頁筆數,1–100,超出 clamp。 |
sort |
enum | created_at |
排序欄位:created_at / name / file_size。 |
order |
enum | desc |
asc / desc。 |
q |
string | — | 搜尋關鍵字(比對 name + description,ILIKE 前綴/包含,見 TDD §5 效能)。 |
target_chip |
enum | — | filter:kl520/kl720/kl630/kl730。 |
source |
enum | — | filter:uploaded/converted/preset。 |
visibility |
enum | — | filter:public/tenant(僅過濾廣播類;private 不在共享庫語意內、傳了視為忽略)。 |
owned |
bool | — | true=只看我的、false=只看別人分享/公開給我的;不帶=全部可見。 |
Response 200(envelope data)
{
"items": [
{
"id": "uuid",
"name": "yolov5s-kl520",
"description": "...",
"target_chip": "kl520",
"file_size": 10485760,
"source": "converted",
"status": "ready",
"visibility": "public",
"owner": { "id": "uuid", "name": "Alice", "is_me": false },
"shared_with_me": false,
"my_access": "viewer",
"created_at": "2026-07-01T00:00:00Z",
"updated_at": "2026-07-02T00:00:00Z"
}
],
"next_cursor": "eyJ...",
"has_more": true
}
欄位說明(權限相關,重要):
owner:只揭露id/name/is_me(不揭露 owner email——非擁有者不該看到他人 email,見 §4 安全)。[需 security 審]。my_access:當前 user 對此模型的有效權限(owner/editor/viewer)。由「owner_user_id 命中 / model_shares.role / visibility 落 viewer」計算,取最高。shared_with_me:是否因 model_shares 命中(供 UI 標「已分享給我」)。[需 Design 確認]標示方式。status:沿用既有 DTO(pending/ready由uploaded_at決定,見models.go toModelResponse)。共享庫只列ready(未 finalize 的不進共享庫,見 TDD §5)。
錯誤碼
| HTTP | code | 情境 |
|---|---|---|
| 400 | VALIDATION_FAILED |
limit / cursor / sort 非法。 |
| 401 | (middleware) | 未登入。 |
| 500/503 | INTERNAL_ERROR |
DB 錯誤(經 WriteDBError 映射,不洩漏 raw)。 |
2. GET /api/models/:id/profile — 模型 profile 頁(公開版詳情)
非擁有者也可看的模型詳情頁。權限檢查為核心:只有「對此 model 有可見性」的 user 能看,且回傳欄位依身份裁剪。
可見性判斷(handler 第一步)
user 能看此 profile ⟺ 命中任一:
owner_user_id == userID(我的)visibility == publicvisibility == tenant且 model.owner.org_id == user.org_idmodel_shares命中(grantee == userID)- preset 模型(公用)
不命中 → 404(不是 403):不揭露「這個 id 存在但你沒權限」,避免 enumeration 洩漏存在性。[需 security 審] 404 vs 403 策略。
Response 200(envelope data)
{
"id": "uuid",
"name": "...",
"description": "...",
"target_chip": "kl520",
"file_size": 10485760,
"source": "converted",
"status": "ready",
"visibility": "public",
"input_shape": [1, 3, 224, 224],
"classes": ["cat", "dog"],
"framework": "onnx",
"owner": { "id": "uuid", "name": "Alice", "is_me": false },
"my_access": "viewer",
"can_download": true,
"created_at": "...",
"updated_at": "...",
"uploaded_at": "..."
}
依身份裁剪的欄位(重要,[需 security 審]):
- 絕不揭露(無論身份):
storage_key、faa_object_key(FAA 內部 key,ADR-017 決策 2 防曝露)、owner email、file_checksum([需 PM 確認]checksum 是否對非 owner 公開)。 can_download:my_access != none時 true。前端據此決定是否顯示下載鈕。實際下載仍走既有GET /api/models/:id/download(該端點需同步加共享權限檢查,見 §3)。
錯誤碼
| HTTP | code | 情境 |
|---|---|---|
| 404 | NOT_FOUND |
model 不存在 或 無可見性(合併,防 enumeration)。 |
3. PATCH /api/models/:id/visibility — 設定公開對象
擁有者設定模型的公開對象。只有 owner(或 editor?[需 PM 確認])能改。
Request
{ "visibility": "public" }
visibility(required):private/tenant/public。tenant但 user 無org_id→ 400(無租戶歸屬不能設 tenant 可見)。[需 PM 確認]demo/無 org user 的行為。
Response 200(envelope data)
{ "id": "uuid", "visibility": "public", "updated_at": "..." }
錯誤碼
| HTTP | code | 情境 |
|---|---|---|
| 400 | VALIDATION_FAILED |
visibility 非法 / 設 tenant 但無 org。 |
| 403 | FORBIDDEN |
非 owner。 |
| 404 | NOT_FOUND |
model 不存在。 |
| 409 | CONFLICT |
model 未 ready(未 finalize 不允許公開,[需 PM 確認])。 |
既有
GET /api/models/:id/download的連帶變更(見 TDD §5):目前是 owner-only(m.OwnerUserID != userID → 403)。共享後需改為「有可見性且can_download→ 放行」。此變更涉及 auth 繞過風險,[需 security 審]。
4. 安全考量摘要(詳見 TDD §6)
| 風險 | 對策 |
|---|---|
| IDOR / enumeration(猜 id 看他人 model) | profile / download 的可見性檢查在 handler 第一步;不命中回 404(不回 403 揭露存在性)。[需 security 審] |
| 越權改 visibility | PATCH visibility 強制 owner 檢查。 |
| 資訊洩漏(email / storage_key / faa_object_key) | DTO 白名單序列化,內部 key 用 json:"-"(沿用 model.Model.FAAObjectKey 既有做法)。 |
| tenant 邊界誤放 | tenant 可見性需 model.owner.org_id == user.org_id 且兩者皆非空;空 org 不落 tenant 可見。 |
| 下載端點權限繞過 | GET /api/models/:id/download 的 owner-only 改共享權限檢查,須與 profile 可見性邏輯共用同一函式(single source of truth,避免兩處判斷不一致)。[需 security 審] |
5. 待三方確認清單(本檔)
| # | 項目 | 待誰 |
|---|---|---|
| S1 | org/tenant 邊界語意(org_id 是否等於租戶;跨 org 是否可能) | PM |
| S2 | editor 是否能改 visibility / 未 ready 能否公開 / checksum 是否對非 owner 公開 | PM |
| S3 | 共享庫 UI 入口(與既有「我的模型」分頁 or 合併 tab)、shared_with_me 標示、profile 頁欄位 |
Design |
| S4 | 404 vs 403 防 enumeration、download 端點權限改動、owner email 不揭露 | security |