fix(local-tool): SPA fallback 改用 Next.js dynamic route shell HTML
根因:點模型卡片 → /models/yolov5-face-detection → server SPA fallback 固定回傳根路徑 /index.html(儀表板) → Next.js CSR router 初始化時 pathname 對不上 → 使用者被跳回儀表板。 修法:spaFallback handler 改成三層 fallback: 1. 精確檔案(/models/index.html 等) 2. Next.js dynamic route shell(把最後一段替換為 _ → /models/_/index.html) 這是 generateStaticParams 產的 placeholder 頁面,Next.js CSR 會從 URL 讀真正的 param 值 3. 根目錄 /index.html(最終 fallback) 這修好了: - 模型詳情頁 /models/:id 不再跳回儀表板 - 裝置詳情頁 /devices/:id 同理 - 工作區裝置頁 /workspace/:deviceId 同理 - Sidebar active 狀態也會正確(因為 pathname 匹配了) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ebe86663b1
commit
819885c85d
@ -144,7 +144,17 @@ func broadcasterLogger(b *logger.Broadcaster) gin.HandlerFunc {
|
||||
}
|
||||
|
||||
// spaFallback tries to serve the exact file from the embedded FS.
|
||||
// If the file doesn't exist, it serves index.html for client-side routing.
|
||||
// If the file doesn't exist, it finds the best matching route shell HTML
|
||||
// for Next.js static export client-side routing.
|
||||
//
|
||||
// Next.js static export with generateStaticParams creates:
|
||||
// /models/index.html — static page
|
||||
// /models/_/index.html — dynamic route shell (placeholder param '_')
|
||||
//
|
||||
// For a request like /models/yolov5-face-detection:
|
||||
// 1. Try exact file → not found
|
||||
// 2. Try /models/_/index.html → found → serve it (Next.js CSR picks up real param from URL)
|
||||
// 3. Fall back to /index.html (root)
|
||||
func spaFallback(staticFS http.FileSystem) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
path := c.Request.URL.Path
|
||||
@ -155,15 +165,31 @@ func spaFallback(staticFS http.FileSystem) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to serve the exact file (e.g., /models/index.html)
|
||||
f, err := staticFS.Open(path)
|
||||
if err == nil {
|
||||
// Try to serve the exact file
|
||||
if f, err := staticFS.Open(path); err == nil {
|
||||
f.Close()
|
||||
http.FileServer(staticFS).ServeHTTP(c.Writer, c.Request)
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to root index.html for SPA routing
|
||||
// Try Next.js dynamic route shell: replace last path segment with '_'
|
||||
// e.g. /models/yolov5 → /models/_/index.html
|
||||
// /devices/kl520-0 → /devices/_/index.html
|
||||
// /workspace/kl520-0 → /workspace/_/index.html
|
||||
segments := strings.Split(strings.TrimRight(path, "/"), "/")
|
||||
if len(segments) >= 2 {
|
||||
segments[len(segments)-1] = "_"
|
||||
shellPath := strings.Join(segments, "/") + "/index.html"
|
||||
if f, err := staticFS.Open(shellPath); err == nil {
|
||||
defer f.Close()
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
c.Status(http.StatusOK)
|
||||
_, _ = io.Copy(c.Writer, f)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback: root index.html
|
||||
index, err := staticFS.Open("/index.html")
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user