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

5.4 KiB
Raw Permalink Blame History

API 規格 — 設備註冊 / 取消註冊(個人設備管理 P0

本檔為 register / unregister 兩個新端點的權威契約。前端對此形狀寫 normalize、後端對此形狀保證回應。既有 GET /api/devicesGET /api/devices/:idPOST /api/devices/:id/unpairapi-spec.md §3、本 P0 不改


通用

  • 認證JWT/OIDC bearer同既有 /api/devices/* route group
  • 識別值:id = device UUID雲端 DB 主鍵。register/unregister 是純雲端 DB 操作、不路由到 local agent故用 UUID對齊 ADR-018 FE-ADB 操作用 UUID、路由操作才用 serial
  • 回應信封:沿用既有 { "success": true, "data": ... }(前端 api client 已 unwrap data)。錯誤為 { "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_atregister 後為 ISO 8601 時間unregister 後為 nullomitempty → 欄位可能不出現,前端 normalize 皆視為 null=未註冊)。

1. POST /api/devices/:id/register — 註冊設備

把裝置從「未註冊」翻成「已註冊」(registered_at NULL → now())。

  • Request body:無。
  • Success200 OK + 更新後 DeviceListItemregistered_at 非 null

行為順序(後端)

步驟 條件 回應
1 缺 UserContext 500 INTERNAL_ERROR
2 :id 400 VALIDATION_FAILED
3 device 不存在 / 已軟刪 404 NOT_FOUND
4 owner_user_id != caller 403 FORBIDDENIDOR 防護)
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 撤 tokendevice 從清單消失unregister 只清 registered_atdevice 仍在清單、顯示為未註冊)。兩端點各走各的,不可合併。詳見 TDD §1。

  • Request body:無。
  • Success200 OK + 更新後 DeviceListItemregistered_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 + DeviceListItemregistered_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 非 ownerIDOR 既有
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 常數 + 前端 i18ndevices.register.error.alreadyRegistered)同步登記。
  • representative 衝突若不想新增碼,可沿用既有 CONFLICT + message 區分;由 backend agent 依既有錯誤碼慣例定,前端據 message/context 提示。

4. 前端呼叫(對齊 device-store.ts 範式)

// 皆用 UUIDDB 操作);不帶 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。