jim800121chen 4f50ad7350 feat(ws): flash-progress WS tunnel proxy(後端,載入模型進度回顯)
flash「載入模型到裝置」後端缺口:flash-progress WS 從 501 stub 換真
tunnel proxy,複用 inference WS 的 path-agnostic handler。

- camera.go registerWebSocketRoutes:加 /ws/devices/:id/flash-progress
  (複用 newWebSocketProxyHandler、掛 wsAuthGroup same-origin cookie)
- stubs.go:刪 flash-progress 501 stub(成對防 radix panic)+ 更新註解
- api.go:更新 wsAuthGroup 註解(現含 inference + flash-progress 兩條裝置級 WS)
- camera_ws_test.go:+3 test(NoForwarder/TunnelDisconnected/CoexistsWithInference)
- all_endpoints_require_auth_test.go:收窄白名單,flash-progress 從 skipped→
  covered 納入「無 cookie 應 401」回歸檢查(守得住證明:移除 auth 即 FAIL)

契約:WS /ws/devices/:id/flash-progress、payload raw {percent,stage,message?,error?}
透明轉發。Reviewer 0C/0M/1Mi 通過(Mi 註解已修)。build/vet/全回歸綠。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 06:30:36 +08:00

82 lines
4.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"github.com/gin-gonic/gin"
)
// registerStubRoutes 註冊尚未實作 / Phase 1 才處理的 HTTP endpoint一律回 501 NOT_IMPLEMENTED。
//
// **為什麼只留這些**Auth / Pairing 補齊 / Devices / Models / GET /clusters /
// system/deps / /storage 都在 B5 補實作(見 auth.go / devices.go / models.go /
// clusters.go / storage.go 各檔Camera / Media 已在 camera.goregisterCameraRoutes
// 換成真正的 tunnel proxy 宣告。這裡只剩:
// - Cloud 裝置記錄(非 tunnel 的 CRUDPhase 1
// - Clusters 寫入類Phase 1
// - ConverterPhase 1
//
// WebSocket 的 501 stub 在 registerWebSocketStubs下方不在此函式。
//
// 讓前端對錯誤路徑能拿到 501 而非 404減少除錯成本。
func registerStubRoutes(g *gin.RouterGroup, _ Deps) {
stub := func(hint string) gin.HandlerFunc {
return func(c *gin.Context) {
WriteNotImplemented(c, hint)
}
}
// --- Cloud-side device records非 tunnel ---
g.GET("/cloud/devices", stub("cloud.devices.list — pending Phase 1"))
g.POST("/cloud/devices/:id/rename", stub("cloud.devices.rename — pending Phase 1"))
// --- Models 其餘 ---
// load-to-device 已在 models.go 註冊為 501 stub為了讓 /api/models/:id/load-to-device 路徑註冊完整)。
// --- Clusters 寫入類 ---
g.POST("/clusters", stub("clusters.create — pending Phase 1"))
g.GET("/clusters/:id", stub("clusters.get — pending Phase 1"))
g.DELETE("/clusters/:id", stub("clusters.delete — pending Phase 1"))
g.POST("/clusters/:id/devices", stub("clusters.add-device — pending Phase 1"))
g.DELETE("/clusters/:id/devices/:deviceId", stub("clusters.remove-device — pending Phase 1"))
g.PUT("/clusters/:id/devices/:deviceId/weight", stub("clusters.set-weight — pending Phase 1"))
g.POST("/clusters/:id/flash", stub("clusters.flash — pending Phase 1"))
g.POST("/clusters/:id/inference/start", stub("clusters.inference.start — pending Phase 1"))
g.POST("/clusters/:id/inference/stop", stub("clusters.inference.stop — pending Phase 1"))
// --- Camera / Media走 tunnel proxy ---
// 已由 registerCameraRoutescamera.go換成真正的 proxy 宣告,不再是 501 stub。
// --- ConverterPhase 1 ---
g.POST("/converter/jobs", stub("converter.submit — pending Phase 1"))
g.GET("/converter/jobs", stub("converter.list — pending Phase 1"))
g.GET("/converter/jobs/:id", stub("converter.get — pending Phase 1"))
g.GET("/converter/jobs/:id/download", stub("converter.download — pending Phase 1"))
}
// registerWebSocketStubs 註冊「尚未實作」的 /ws/* endpoint一律回 501讓前端能收到
// 明確錯誤,由 B7 補齊。
//
// WS tunnel proxy 的基礎設施Forwarder.ForwardWebSocket、api-server 端 Hijack + 雙向
// io.Copy已實作見 forwarder.go §ForwardWebSocket、proxy.go newWebSocketProxyHandler
// 且 /ws/devices/:id/inference 與 /ws/devices/:id/flash-progress 已由
// registerWebSocketRoutes 換成真正的 WS tunnel proxy。這裡只剩尚未接上 proxy 的其餘
// /ws/* 端點events / server-logs / system / clusters / pairing
//
// 注意ws endpoint 在 /ws 而非 /api/ws所以由 NewRouter 直接註冊而非 apiGroup。
func registerWebSocketStubs(r *gin.Engine) {
stub := func(hint string) gin.HandlerFunc {
return func(c *gin.Context) {
WriteNotImplemented(c, hint)
}
}
// 用 GETWebSocket upgrade 的初始 HTTP request
r.GET("/ws/devices/events", stub("ws.devices.events — pending B7"))
// /ws/devices/:id/inference 與 /ws/devices/:id/flash-progress 已由
// registerWebSocketRoutes 換成真正的 WS tunnel proxy掛在 AuthMiddleware group 內,
// 走 same-origin cookie 認證)。不在此註冊 stub避免 gin radix tree 同路徑重複註冊 panic。
r.GET("/ws/server-logs", stub("ws.server-logs — pending B7"))
r.GET("/ws/system", stub("ws.system — pending B7"))
r.GET("/ws/clusters/:id/inference", stub("ws.clusters.inference — pending B7"))
r.GET("/ws/clusters/:id/flash-progress", stub("ws.clusters.flash — pending B7"))
r.GET("/ws/pairing/status", stub("ws.pairing.status — pending B7"))
}