fix(admin-web): 部署版本守卫,彻底防止 stale bundle 崩溃
方案: - 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 <noreply@anthropic.com>
This commit is contained in:
parent
36b899d7ed
commit
300d55ff14
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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' } },
|
||||
);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<AuthProvider>
|
||||
<DeployGuard />
|
||||
{children}
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue