HUAN 讙 · 開發者

協定

Admin、Server、Worker 與 Device 共用 @huan/protocol 的結構定義。型別只定義一次。

API 版本

所有 REST 端點掛在 /api/v1 之下:

import { API_PREFIX, PROTOCOL_VERSION } from "@huan/protocol";

PROTOCOL_VERSION 是裝置與 Server 之間的通訊協定版本,裝置在 WebSocket handshake 與 heartbeat 中回報,Server 用它判斷相容性。

認證

對象方式
瀏覽器Cookie: huan_session=<不透明 token>(HttpOnly、SameSite=Lax、正式環境加 Secure)
裝置Authorization: Bearer <deviceId>.<secret>

錯誤

所有錯誤回應共用同一個結構:

{ code: string, message: string, details?: Record<string, unknown> }

code 是穩定的機器可讀識別字,message 是使用者可讀的繁體中文說明。

常見的 codeunauthorizedforbiddennot_foundvalidation_failedrate_limitedtotp_requiredinvalid_credentialsconflictasset_in_useasset_unavailablepairing_expired

OpenAPI

規格由 zod 結構自動產生,不是人工維護的第二份文件:

端點說明
GET /api/v1/openapi.json原始規格
GET /api/v1/docs互動式 API 文件(非正式環境)

因此 API 文件不可能和實作不同步——它們是同一份結構的兩種輸出。

WebSocket

裝置通道 /api/v1/devices/socket

Server 送出:

{
	type: "hello";
	serverTime;
	protocolVersion;
	desiredVersion;
}
{
	type: "desired_state_changed";
	version: number;
}
{
	type: "command";
	commandId: string;
	command: "force_sync" | "restart_player" | "unbind";
}
{
	type: "pong";
	serverTime;
}

裝置送出:

{ type: "ping" }
{ type: "heartbeat"; reported: ReportedState }
{ type: "command_result"; commandId: string; ok: boolean; message?: string }

WebSocket 不傳輸任何素材。 它只說「有東西變了」,真正的狀態一律回到 REST 取得(ADR-0001)。

每一個進來的訊框都用結構驗證。不合法的訊框會被丟棄並記錄,不會拋例外

後台通道 /api/v1/admin/socket

以 session cookie 認證,推播 device_changedmedia_changed,讓後台不需要重新整理就能看到裝置上下線與轉檔完成。

裝置同步

簽章網址不會寫進目標狀態,因為目標狀態會被快取在裝置本機,而簽章網址只有 15 分鐘的壽命。裝置在需要下載時才即時索取。

相容性

  • REST 走路徑版本(/api/v1)。破壞性的變更會開 /api/v2
  • WebSocket 訊息以 type 做可辨識聯集,新增訊息類型不會影響舊裝置——它們會驗證失敗並丟棄,而不是崩潰。
  • 裝置回報 appVersionprotocolVersion,Server 可以據此拒絕或降級處理過舊的裝置。
  • 新增的欄位一律是選填,或帶有預設值。