From 300d55ff1483f9506f3a6a962890139bc71d7ad1 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 4 Mar 2026 18:57:12 -0800 Subject: [PATCH] =?UTF-8?q?fix(admin-web):=20=E9=83=A8=E7=BD=B2=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=AE=88=E5=8D=AB=EF=BC=8C=E5=BD=BB=E5=BA=95=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=20stale=20bundle=20=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 方案: - next.config.ts 构建时注入 NEXT_PUBLIC_BUILD_TIME 时间戳 - 新增 /api/version 路由,返回服务器当前 build 时间戳 - DeployGuard 组件每 3 分钟轮询 /api/version, 发现版本变化立即 window.location.reload(),用户完全无感知 - global-error / admin error 边界仅做最后兜底(静默 reload) Co-Authored-By: Claude Sonnet 4.6 --- frontend/admin-web/next.config.ts | 4 ++ .../admin-web/src/app/api/version/route.ts | 8 ++++ frontend/admin-web/src/app/providers.tsx | 6 ++- frontend/admin-web/src/lib/deploy-guard.tsx | 40 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 frontend/admin-web/src/app/api/version/route.ts create mode 100644 frontend/admin-web/src/lib/deploy-guard.tsx diff --git a/frontend/admin-web/next.config.ts b/frontend/admin-web/next.config.ts index c0ea6e9..8f91376 100644 --- a/frontend/admin-web/next.config.ts +++ b/frontend/admin-web/next.config.ts @@ -2,6 +2,10 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'standalone', + // 构建时注入时间戳,客户端和 /api/version 均使用此值判断版本是否更新 + env: { + NEXT_PUBLIC_BUILD_TIME: String(Date.now()), + }, reactStrictMode: true, images: { remotePatterns: [ diff --git a/frontend/admin-web/src/app/api/version/route.ts b/frontend/admin-web/src/app/api/version/route.ts new file mode 100644 index 0000000..7837d9c --- /dev/null +++ b/frontend/admin-web/src/app/api/version/route.ts @@ -0,0 +1,8 @@ +import { NextResponse } from 'next/server'; + +export function GET() { + return NextResponse.json( + { buildTime: process.env.NEXT_PUBLIC_BUILD_TIME }, + { headers: { 'Cache-Control': 'no-store' } }, + ); +} diff --git a/frontend/admin-web/src/app/providers.tsx b/frontend/admin-web/src/app/providers.tsx index 77bf7b4..9e1d7b2 100644 --- a/frontend/admin-web/src/app/providers.tsx +++ b/frontend/admin-web/src/app/providers.tsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AuthProvider } from '@/lib/auth-context'; +import { DeployGuard } from '@/lib/deploy-guard'; export function Providers({ children }: { children: React.ReactNode }) { const [queryClient] = useState( @@ -20,7 +21,10 @@ export function Providers({ children }: { children: React.ReactNode }) { return ( - {children} + + + {children} + ); } diff --git a/frontend/admin-web/src/lib/deploy-guard.tsx b/frontend/admin-web/src/lib/deploy-guard.tsx new file mode 100644 index 0000000..01d2804 --- /dev/null +++ b/frontend/admin-web/src/lib/deploy-guard.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { useEffect } from 'react'; + +const BUILD_TIME = process.env.NEXT_PUBLIC_BUILD_TIME ?? ''; +const POLL_INTERVAL = 3 * 60 * 1000; // 3 分钟 + +/** + * 部署版本守卫 + * + * 每 3 分钟向 /api/version 轮询服务器 build 时间戳。 + * 若与当前 bundle 的时间戳不一致,说明有新版本已部署, + * 立即静默刷新页面,用户无感知。 + * + * 解决:重新部署后浏览器持有旧 bundle → Next.js Server Action + * ID 不匹配 → "Application error" 崩溃。 + */ +export function DeployGuard() { + useEffect(() => { + if (!BUILD_TIME) return; + + const check = async () => { + try { + const res = await fetch('/api/version', { cache: 'no-store' }); + if (!res.ok) return; + const { buildTime } = await res.json(); + if (buildTime && buildTime !== BUILD_TIME) { + window.location.reload(); + } + } catch { + // 网络错误忽略,下次轮询再试 + } + }; + + const timer = setInterval(check, POLL_INTERVAL); + return () => clearInterval(timer); + }, []); + + return null; +}