visionA/docs/autoflow/04-architecture/api/api-model-sharing.md
jim800121chen f6d15b7b14 docs(arch): B 設備管理 + C 模型共享 + camera ADR-020 規劃文件
- 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>
2026-08-02 16:31:38 +08:00

8.6 KiB
Raw Permalink Blame History

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 sessionBFFapi-spec.mdhandler 從 UserContextuserID / 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_idmigration 0001 已有欄位、domain 尚未使用,見 TDD §2tenant visibility = 同 org_id 可見。[需 PM 確認] org 邊界語意。

與既有 /api/models 的分工:既有 GET /api/modelsapi-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 每頁筆數1100超出 clamp。
sort enum created_at 排序欄位:created_at / name / file_size
order enum desc asc / desc
q string 搜尋關鍵字(比對 name + descriptionILIKE 前綴/包含,見 TDD §5 效能)。
target_chip enum filterkl520/kl720/kl630/kl730
source enum filteruploaded/converted/preset
visibility enum filterpublic/tenant(僅過濾廣播類;private 不在共享庫語意內、傳了視為忽略)。
owned bool true=只看我的、false=只看別人分享/公開給我的;不帶=全部可見。

Response 200envelope 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:沿用既有 DTOpending/readyuploaded_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 ⟺ 命中任一:

  1. owner_user_id == userID(我的)
  2. visibility == public
  3. visibility == tenant 且 model.owner.org_id == user.org_id
  4. model_shares 命中grantee == userID
  5. preset 模型(公用)

不命中 → 404(不是 403不揭露「這個 id 存在但你沒權限」,避免 enumeration 洩漏存在性。[需 security 審] 404 vs 403 策略。

Response 200envelope 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_keyfaa_object_keyFAA 內部 keyADR-017 決策 2 防曝露、owner email、file_checksum[需 PM 確認] checksum 是否對非 owner 公開)。
  • can_downloadmy_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" }
  • visibilityrequiredprivate / tenant / public
  • tenant 但 user 無 org_id → 400無租戶歸屬不能設 tenant 可見)。[需 PM 確認] demo/無 org user 的行為。

Response 200envelope 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-onlym.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 合併 tabshared_with_me 標示、profile 頁欄位 Design
S4 404 vs 403 防 enumeration、download 端點權限改動、owner email 不揭露 security