B 設備管理(feature-device-mgmt-tdd): - POST /api/devices/:id/register + /unregister(owner 檢查 + representative 擋 + 已註冊擋 + SetRegistered 單欄翻轉,不碰 unpair 軟刪) - error codes ALREADY_REGISTERED / REPRESENTATIVE_DEVICE(409) - 不需 migration(registered_at 欄/index/讀寫已在 0005) C 模型共享(feature-model-sharing-tdd,security 深審 APPROVE): - migration 0006:models.visibility enum DEFAULT 'private'(零行為改變)+ model_shares 表 - canAccessModel single source(owner ∪ share ∪ public ∪ tenant):profile + download 共用 - GET /library(cursor keyset)/ GET /:id/profile(404 防列舉、GetWithOwner join name 不洩 email) / PATCH /:id/visibility(owner-only)/ shares CRUD / download 放寬 - tenant 因 OIDC 無 org claim 留 stub(恆空、安全預設;補 org claim 需重送 security 深審) reviewer 通過(B 三條紅線 / C security APPROVE 無 C/M)。130 dbtest 全綠、gosec 新檔 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
3.4 KiB
SQL
50 lines
3.4 KiB
SQL
-- 0006_model_sharing.up.sql
|
||
--
|
||
-- 模型共享(Model Sharing)L 級新功能。在既有 owner-only 模型庫上,疊加兩個正交維度:
|
||
-- (1) visibility 廣播欄(private / tenant / public)— models 表加 enum 欄。
|
||
-- (2) model_shares 點對點分享表(ADR-017 決策 3 B1)— 分享給特定 user。
|
||
--
|
||
-- 對齊:docs/autoflow/04-architecture/feature-model-sharing-tdd.md §3、
|
||
-- docs/autoflow/04-architecture/api/api-model-sharing.md、
|
||
-- docs/autoflow/04-architecture/adr/adr-017-model-library-access.md 決策 3。
|
||
--
|
||
-- 環境事實(與 0001–0005 相同,已驗證):PostgreSQL 14.23,gen_random_uuid() 內建可直接用。
|
||
--
|
||
-- ★關鍵相容性:models.visibility DEFAULT 'private' → 既有所有 model 遷移後維持 owner-only
|
||
-- 語意,零行為改變。使用者要主動 PATCH visibility 才會公開。
|
||
|
||
-- ── (1) models 加 visibility 欄(廣播式公開對象)─────────────────────────────
|
||
-- 'private'(僅擁有者,= 現況預設)| 'tenant'(同租戶可見)| 'public'(全平台可見)
|
||
-- 全部既有 row 加欄後為 'private'(DEFAULT),語意完全等同遷移前的 owner-only。
|
||
ALTER TABLE models ADD COLUMN visibility TEXT NOT NULL DEFAULT 'private';
|
||
ALTER TABLE models ADD CONSTRAINT chk_models_visibility
|
||
CHECK (visibility IN ('private', 'tenant', 'public'));
|
||
|
||
-- ── (2) model_shares 表(點對點分享,ADR-017 決策 3 B1,本功能沿用不重造)──────
|
||
-- role:'viewer'(可 list/get/download)| 'editor'(可改 metadata;本期讀取端用,寫入權後續)。
|
||
-- PK (model_id, grantee_user_id):同一 model 對同一 grantee 只有一筆分享(重複分享 = upsert)。
|
||
-- FK ON DELETE CASCADE:model 硬刪時連帶清 share(雖然本系統 model 為軟刪,CASCADE 為防禦性
|
||
-- 一致——若未來真硬刪不留孤兒列;軟刪時 share 保留,由查詢端 join models.deleted_at 過濾)。
|
||
CREATE TABLE model_shares (
|
||
model_id UUID NOT NULL REFERENCES models(id) ON DELETE CASCADE,
|
||
grantee_user_id UUID NOT NULL REFERENCES users(id),
|
||
role TEXT NOT NULL DEFAULT 'viewer',
|
||
granted_by UUID NOT NULL REFERENCES users(id),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
PRIMARY KEY (model_id, grantee_user_id),
|
||
CONSTRAINT chk_model_shares_role CHECK (role IN ('viewer', 'editor'))
|
||
);
|
||
|
||
-- grantee 反查(共享庫「分享給我」predicate 的 EXISTS 子查走此 index)。
|
||
CREATE INDEX idx_model_shares_grantee ON model_shares (grantee_user_id);
|
||
|
||
-- ── (3) 共享庫查詢用 index ───────────────────────────────────────────────────
|
||
-- public 全平台可見列表:high-selectivity partial index(沿用既有 models index 的
|
||
-- `WHERE deleted_at IS NULL` 慣例)。只索引 public 且未刪除且已上傳(ready)的 model,
|
||
-- 共享庫預設按 created_at DESC 排序、此 index 直接覆蓋該掃描。
|
||
CREATE INDEX idx_models_public_active ON models (created_at DESC)
|
||
WHERE deleted_at IS NULL AND visibility = 'public' AND uploaded_at IS NOT NULL;
|
||
|
||
-- tenant 可見需 join users 取 owner.org_id;users 主鍵 join 成本低,
|
||
-- owner 維度沿用既有 idx_models_owner_active,不另建。
|