- 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>
5.4 KiB
5.4 KiB
API 規格 — 設備註冊 / 取消註冊(個人設備管理 P0)
- 上位:
../feature-device-mgmt-tdd.md、api-spec.md§3 Devices - 狀態:Draft(契約,contract-first single source of truth)
- 最後更新:2026-08-02
本檔為 register / unregister 兩個新端點的權威契約。前端對此形狀寫 normalize、後端對此形狀保證回應。既有
GET /api/devices、GET /api/devices/:id、POST /api/devices/:id/unpair見api-spec.md§3、本 P0 不改。
通用
- 認證:JWT/OIDC bearer(同既有
/api/devices/*route group)。 - 識別值:
:id= device UUID(雲端 DB 主鍵)。register/unregister 是純雲端 DB 操作、不路由到 local agent,故用 UUID(對齊 ADR-018 FE-A:DB 操作用 UUID、路由操作才用 serial)。 - 回應信封:沿用既有
{ "success": true, "data": ... }(前端 api client 已 unwrapdata)。錯誤為{ "success": false, "error": { "code", "message" } }。
DeviceListItem(回應 data 形狀,沿用既有,snake_case)
register/unregister 成功皆回更新後的單筆 DeviceListItem(與 GET /api/devices/:id 同形狀):
{
"id": "uuid",
"name": "KL520 #A1B2",
"device_type": "kl520",
"serial_number": "0x1234ABCD",
"agent_id": "uuid",
"registered_at": "2026-08-02T10:00:00Z",
"remote_status": "online",
"last_seen_at": "2026-08-02T10:00:00Z",
"last_connected_at": null,
"status": "connected",
"tunnel_online": true,
"created_at": "2026-08-01T00:00:00Z",
"updated_at": "2026-08-02T10:00:00Z"
}
registered_at:register 後為 ISO 8601 時間;unregister 後為null(omitempty → 欄位可能不出現,前端 normalize 皆視為 null=未註冊)。
1. POST /api/devices/:id/register — 註冊設備
把裝置從「未註冊」翻成「已註冊」(registered_at NULL → now())。
- Request body:無。
- Success:
200 OK+ 更新後 DeviceListItem(registered_at非 null)。
行為順序(後端)
| 步驟 | 條件 | 回應 |
|---|---|---|
| 1 | 缺 UserContext | 500 INTERNAL_ERROR |
| 2 | :id 空 |
400 VALIDATION_FAILED |
| 3 | device 不存在 / 已軟刪 | 404 NOT_FOUND |
| 4 | owner_user_id != caller |
403 FORBIDDEN(IDOR 防護) |
| 5 | is_representative == true |
409 CONFLICT(代表 device 不可註冊) |
| 6 | registered_at != null(已註冊) |
409 ALREADY_REGISTERED |
| 7 | 正常 | 200 + DeviceListItem |
範例
POST /api/devices/7f3a.../register
→ 200 { "success": true, "data": { ...,"registered_at":"2026-08-02T10:00:00Z" } }
(已註冊再打)
→ 409 { "success": false, "error": { "code":"ALREADY_REGISTERED","message":"device already registered" } }
2. POST /api/devices/:id/unregister — 取消註冊(退回未註冊)
把裝置退回「未註冊」(registered_at → NULL),保留裝置列、不軟刪、不撤 token。
⚠️ 與
unpair完全不同:unpair 是軟刪整台 + cascade 撤 token(device 從清單消失);unregister 只清registered_at(device 仍在清單、顯示為未註冊)。兩端點各走各的,不可合併。詳見 TDD §1。
- Request body:無。
- Success:
200 OK+ 更新後 DeviceListItem(registered_at= null)。
行為順序(後端)
| 步驟 | 條件 | 回應 |
|---|---|---|
| 1 | 缺 UserContext | 500 INTERNAL_ERROR |
| 2 | :id 空 |
400 VALIDATION_FAILED |
| 3 | device 不存在 / 已軟刪 | 404 NOT_FOUND |
| 4 | owner_user_id != caller |
403 FORBIDDEN |
| 5 | is_representative == true |
409 CONFLICT |
| 6 | 已是未註冊(registered_at == null) |
200(冪等 no-op,非錯誤) |
| 7 | 正常 | 200 + DeviceListItem(registered_at=null) |
範例
POST /api/devices/7f3a.../unregister
→ 200 { "success": true, "data": { ...,"registered_at": null } }
(device 仍存在於 GET /api/devices)
3. 錯誤碼
| code | HTTP | 情境 | 新增? |
|---|---|---|---|
VALIDATION_FAILED |
400 | id 空 | 既有 |
FORBIDDEN |
403 | 非 owner(IDOR) | 既有 |
NOT_FOUND |
404 | device 不存在/軟刪 | 既有 |
CONFLICT |
409 | representative device 不可註冊/取消 | 既有(或沿用既有 CONFLICT 碼) |
ALREADY_REGISTERED |
409 | register 時已註冊 | 新增 |
INTERNAL_ERROR |
500 | 缺 UserContext / DB 錯 | 既有(DB down 經 errors.go 映射 503) |
ALREADY_REGISTERED為本功能新增錯誤碼,需在後端 errors 常數 + 前端 i18n(devices.register.error.alreadyRegistered)同步登記。- representative 衝突若不想新增碼,可沿用既有
CONFLICT+ message 區分;由 backend agent 依既有錯誤碼慣例定,前端據 message/context 提示。
4. 前端呼叫(對齊 device-store.ts 範式)
// 皆用 UUID(DB 操作);不帶 serial。
registerDevice(id): POST /api/devices/{id}/register → 更新 store 該筆 registeredAt
unregisterDevice(id): POST /api/devices/{id}/unregister → 清 store 該筆 registeredAt(保留列)
- 成功後就地更新 store 對應 device 的
registeredAt(避免 refetch 延遲,比照既有 unpairDevice 就地移除的範式),或 refetch 該筆。 - 409
ALREADY_REGISTERED→ toast「已註冊」+ refetch。 - 不像 unpair 從 list 移除——unregister 只改欄、device 留在 list。