前端: - src/srv/api → src/api,按域分文件夹(auth/cluster/host/storage/deployment/collector) - 新增 http.ts 引擎(http.get/post/put/delete),域模块改用语义化调用 - index.html 增加 favicon(匹配应用 logo 的 emerald + Activity 脉冲) 后端分层增强(保留包名): - 新增 repository/:从 models 拆出 JsonDB 持久化层(InitDB/GetDB/Shutdown/RequestSave) - 新增 dto/:从 controllers 抽出各域请求契约(带 binding 校验) - 新增 pkg/crypto、pkg/jwt:从 utils 拆出安全原语(外部 jwt 别名 jwtv5 避免冲突) - models 只留领域实体 + sanitizer;utils 只留 gin/应用相关(response/errors) v2 优化移植与缺陷修复: - 移植密钥分离/SSH TOFU/并发写安全/Loki 生产级配置/Docker 加固 - 修复 deployment.go 并发丢更新、Helm values 重复键、EncryptField 哨兵、loki-gateway 端点等审计缺陷 - 实现真正的优雅关停(main.go http.Server + signal + repository.Shutdown) - 修复登录契约:后端返回 accessToken,前端 auth.ts/Login.tsx 对齐 - 补 crypto/jwt/repository 单元测试(含并发写回归) 验证: go build/vet/test 全绿;npm run build 通过;登录→鉴权→优雅关停冒烟通过 Co-Authored-By: Claude <noreply@anthropic.com>
99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
import { lazy, Suspense, memo } from 'react'
|
|
import { Routes, Route, Navigate } from 'react-router'
|
|
import { Toaster } from '@/components/ui/sonner'
|
|
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
|
|
|
// 路由守卫组件 - 独立定义,便于复用
|
|
const RequireAuth = memo(function RequireAuth({ children }: { children: React.ReactNode }) {
|
|
const token = localStorage.getItem('token')
|
|
if (!token) {
|
|
return <Navigate to="/login" replace />
|
|
}
|
|
// 动态导入Layout以避免在登录页面加载它
|
|
return <LazyLayout>{children}</LazyLayout>
|
|
})
|
|
|
|
// 懒加载页面组件 - 实现代码分割
|
|
const Login = lazy(() => import('@/pages/Login'))
|
|
const Dashboard = lazy(() => import('@/pages/Dashboard'))
|
|
const Clusters = lazy(() => import('@/pages/Clusters'))
|
|
const Hosts = lazy(() => import('@/pages/Hosts'))
|
|
const Storage = lazy(() => import('@/pages/Storage'))
|
|
const Deployments = lazy(() => import('@/pages/Deployments'))
|
|
const Collectors = lazy(() => import('@/pages/Collectors'))
|
|
const Settings = lazy(() => import('@/pages/Settings'))
|
|
const Layout = lazy(() => import('@/components/Layout'))
|
|
|
|
// 懒加载布局包装器
|
|
function LazyLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<Suspense fallback={<PageLoadingFallback />}>
|
|
<Layout>{children}</Layout>
|
|
</Suspense>
|
|
)
|
|
}
|
|
|
|
// 页面加载中占位
|
|
function PageLoadingFallback() {
|
|
return (
|
|
<div className="h-screen w-screen flex items-center justify-center bg-slate-50 dark:bg-slate-950">
|
|
<LoadingSpinner size="lg" text="页面加载中..." />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 路由配置 - 集中管理路由定义
|
|
interface RouteConfig {
|
|
path: string
|
|
element: React.ReactNode
|
|
auth?: boolean
|
|
}
|
|
|
|
const routes: RouteConfig[] = [
|
|
{ path: '/login', element: <Login />, auth: false },
|
|
{ path: '/', element: <Dashboard />, auth: true },
|
|
{ path: '/clusters', element: <Clusters />, auth: true },
|
|
{ path: '/hosts', element: <Hosts />, auth: true },
|
|
{ path: '/storage', element: <Storage />, auth: true },
|
|
{ path: '/deployments', element: <Deployments />, auth: true },
|
|
{ path: '/collectors', element: <Collectors />, auth: true },
|
|
{ path: '/settings', element: <Settings />, auth: true },
|
|
]
|
|
|
|
/**
|
|
* App 根组件
|
|
*
|
|
* 优化点:
|
|
* 1. 所有页面使用 React.lazy 懒加载,实现代码分割
|
|
* 2. 路由守卫独立为 memo 组件
|
|
* 3. Suspense 统一处理加载状态
|
|
* 4. 路由配置集中管理
|
|
*/
|
|
const App = memo(function App() {
|
|
return (
|
|
<>
|
|
<Routes>
|
|
{routes.map((route) => (
|
|
<Route
|
|
key={route.path}
|
|
path={route.path}
|
|
element={
|
|
route.auth ? (
|
|
<RequireAuth>{route.element}</RequireAuth>
|
|
) : (
|
|
<Suspense fallback={<PageLoadingFallback />}>
|
|
{route.element}
|
|
</Suspense>
|
|
)
|
|
}
|
|
/>
|
|
))}
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
<Toaster />
|
|
</>
|
|
)
|
|
})
|
|
|
|
export default App
|