範圍與掛載路徑
每張自訂資料表都掛在一個組織範圍(scope)下。範圍同時決定 URL 的掛載形狀與第一層存取邊界;真正能否讀、寫或管理資料,仍由該端點的驗證 dependency 與資料表 ACL 一起判斷。共用 public multipart chunk leg 是下方說明的刻意例外:它以持有 session capability 為 gate,不使用 user auth。
三種掛載範圍
| 模組 | 基底路徑 | 約略路由數 |
|---|---|---|
| 自訂資料表 — 聊天室範圍 | /private/module/custom_tables/chatroom/{chatroom_id} | 105 |
| 自訂資料表 — 部門範圍 | /private/module/custom_tables/department/{department_id} | 112 |
| 自訂資料表 — 公司範圍 | /private/module/custom_tables/company | 110 |
Commands(company 路徑含 {company_id}) | …/{scopeWithId}/commands… | 9×3 |
| Public-read tokens(不含 scope id) | …/{scopeName}/tables/{table_id}/public-read-tokens… | 3×3 |
| 附件(掛載於三種範圍,不含 scope id) | …/{scopeName}/tables/{table_id}/…(multipart blobs/* ×4,加上 records/{record_id}/attachments/{blob_id}/download) | 5×3 |
| 共用 multipart chunk leg | /public/module/etl/multipart/chunk | 1 |
ETL tables 是另一個模組。本手冊只保留這條 public chunk 路徑,因為 Custom Table 附件上傳會重用它;不要把 /private/module/etl/… 或其他 public ETL 路由當成 Custom Tables。
功能矩陣:哪個 scope 有哪些 family
大多數 record、schema、view、tag、rule、trigger、IaC、import、bulk、history、trash、lifecycle、user-grant、client-permission 與 callback-token 的 path tail 都掛在三種 scope。客戶端不該自行猜測的例外如下:
| Family | Chatroom | Department | Company |
|---|---|---|---|
| 核心 table/record/schema/自動化 tails | 有 | 有 | 有 |
GET …/tables/shared | 有 | 無 | 無 |
Chatroom table 的 department-permissions/{department_id} | 有 | 無 | 無 |
| Permission-request approve/reject | 有 | 無 | 無 |
Department table 的 chatroom-permissions | 無 | 有 | 無 |
Department/company 的 department-permissions/{target_department_id} | 無 | 有 | 有 |
| Table moderators | 無 | 有 | 有 |
| Publish sessions | 無 | 有 | 有 |
PUT …/columns/{column_id} 別名 | 無 | 無 | 有 |
Commands(company/{company_id}/commands) | 有 | 有 | 有 |
附件與 public-read tokens({scopeName},不含 id) | 有 | 有 | 有 |
授權不會改變擁有範圍的掛載:部門表被授權給聊天室後,record API 仍走 department/{department_id}。驗證 gate 與 union 契約見範圍、掛載與跨範圍存取。
組織上各自代表什麼
- 聊天室(chatroom):資料表屬於一個聊天室,路徑必須帶
chatroom_id。適合由該聊天室成員共同操作的資料;入口權限通常從加入聊天室或聊天室管理權開始,再套用表級 ACL。 - 部門(department):資料表屬於一個部門,路徑必須帶
department_id。適合部門層級的共同資料;部門存取與表級 ACL 是兩層不同的判斷。 - 公司(company):資料表屬於目前使用者的公司。路徑本身不帶
company_id,但仍會驗證使用者的公司身分、管理權或表級權限。
選 scope 時先問「這份資料由哪個組織單位擁有」,不要只為了縮短 URL 而選 company。資料表建立後的分享需求,應交給 ACL 與 grants,而不是複製到另一個 scope。
各範圍的驗證風格速覽
下表用這份快速入門會碰到的端點說明命名規律;dependency 名稱是後端的授權門檻,不是要放進 token 的 OAuth scope。
| 範圍 | 建立/列出資料表 | 資料列讀寫 | 詳細資料 |
|---|---|---|---|
| 聊天室 | ChatRoomAccessRequired/ChatRoomJoinedRequired | CustomTableReadRequired、CustomTableInsertRequired、CustomTableEditRequired | 資料表參考、資料列參考、權限與 ACL |
| 部門 | DepartmentAccessRequired | CustomTableReadRequired、CustomTableInsertRequired、CustomTableEditRequired | 資料表參考、資料列參考、權限與 ACL |
| 公司 | CompanyAccessRequired | CustomTableReadRequired、CustomTableInsertRequired、CustomTableEditRequired | 資料表參考、資料列參考、權限與 ACL |
不同操作的門檻並不完全相同;例如建立欄位、執行診斷與解析 links 在三個 scope 間有特例。實作前請以每張 reference card 的 Auth 與 scope matrix 為準。
不帶 scope ID 的路由例外
警告:migration 查詢與全部五個附件路由只掛在固定、不含 ID 的 scope 名稱下。即使 scope 是 chatroom 或 department,也不能插入
chatroom_id或department_id。
Migration ID 在全域唯一,因此兩個查詢形狀是:
/private/module/custom_tables/{scopeName}/migrations/{migration_id}/status
/private/module/custom_tables/{scopeName}/migrations/{migration_id}/diagnosis-results附件也使用 {scopeName};例如聊天室 multipart 初始化是:
/private/module/custom_tables/chatroom/tables/{table_id}/blobs/multipart/init它不是 /chatroom/{chatroom_id}/tables/...。產生 URL 時,只有一般 scoped route 才展開成 chatroom/{chatroom_id}、department/{department_id} 或 company;migration 與附件則只替換成 chatroom、department 或 company。完整形狀請見 migration 參考與附件參考。
Multipart 的 chunk upload 本身既不是 private scope mount,也不是上述每個 scope 的五條附件 routes 之一:
POST /public/module/etl/multipart/chunk這條 route 沒有 user-auth dependency。Private scoped init 會建立高熵、短期 session_id;持有此值就是上傳 chunks 的 capability,因此不能放進 log、analytics 或 URL。它的 multipart/form-data 欄位是 session_id、zero-based chunk_index、binary chunk,以及選填、針對原始 bytes 的小寫 SHA-256 chunk_hash。非最後 chunk 必須等於 chunk_size;最後 chunk 必須等於精確剩餘 bytes。Response 回 uploaded indexes/progress;errors 包含 index/size/hash 無效(400)、session 不存在/過期(404)、重複/衝突(409)、size refusal(413)與 storage/cache failure(503)。Status、complete、abort、掛到 record 與 download 仍是具有 user/table authorization 的 private scoped operations。
找到別人分享給你的表
有兩條 discovery 路由完全不帶 scope 路徑段——連 scope 名稱都沒有——因為呼叫者根本不知道是哪個部門或哪個房間分享給他:
/private/module/custom_tables/shared-with-me
/private/module/custom_tables/tables/resolve?name=<精確名稱>兩條都以 get_current_user 作為唯一閘門:沒有角色檢查、沒有 scope 成員資格 dependency,租戶邊界改由查詢內部逐列把關,而不是由路由把關。
tables.sharedWithMe 回答「有什麼是從我自己的 scope 之外送到我手上的」:per-user grant、我所屬部門的 grant、透過我所屬活房間的 internal chatroom grant,或分享給全部部門的部門表。每一筆都帶著放行的 sources 與我解析後的權限,而 limit 上限是 200——不是各 scope 表清單的 1000。它與那些清單互補、不是超集:自己部門的表、company scope 的表、所屬房間的表都被刻意排除,想要「我能碰到的全部」必須把兩者聯集。
tables.resolveByName 把精確名稱轉成 id,範圍同時涵蓋 company 與 department scope。比對是逐位元組的(utf8mb4_bin),而 per-company 的名稱唯一鍵同時涵蓋兩種 scope,因此最多只會有一張表命中;所有落空——不存在、讀不到、在垃圾桶、別家公司、chatroom scope——都是同一個 404 {"detail": "Table not found"}。用它取代逐環境硬寫 id,也用它補上 discovery 唯一補不了的缺口:角色低於 CAN_MANAGE_COMPANY 的人在 company scope 表上的 per-user grant,在任何清單上都找不到。
從任一回應取得 table_id 之後,改走該表擁有 scope 的 by-id 路由——兩個回應都不含 schema、settings 或資料列。