为 new-api (LLM API 网关) 构建独立前端管理面板: - React 18 + TypeScript + Vite + Ant Design 5 前端 - Node.js + Express + better-sqlite3 BFF 后端 - 登录页: 站点选择器 + UserID + AccessToken 认证 - 仪表盘: 用户信息、额度环形图、7天趋势图、模型饼图、令牌概览、日志时间线 - 账单页: 筛选日志表格、模型消耗柱状图、充值记录、CSV/PDF 导出(HMAC签名) - 管理员站点管理: 站点 CRUD - API 代理: 多站点切换,会话管理 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { useEffect } from 'react'
|
|
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
|
|
import { ConfigProvider, App as AntApp, Spin } from 'antd'
|
|
import zhCN from 'antd/locale/zh_CN'
|
|
import { lightTheme } from '@/utils/theme'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { authApi } from '@/api/auth'
|
|
import Layout from '@/components/Layout'
|
|
import Login from '@/pages/Login'
|
|
import Dashboard from '@/pages/Dashboard'
|
|
import Billing from '@/pages/Billing'
|
|
import SiteManagement from '@/pages/admin/SiteManagement'
|
|
|
|
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
|
const { isLoggedIn, loading } = useAuthStore()
|
|
|
|
if (loading) {
|
|
return (
|
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
|
<Spin size="large" tip="Loading..." />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return isLoggedIn ? <>{children}</> : <Navigate to="/login" />
|
|
}
|
|
|
|
function SessionRestore({ children }: { children: React.ReactNode }) {
|
|
const { sessionToken, login, logout, setLoading } = useAuthStore()
|
|
|
|
useEffect(() => {
|
|
if (sessionToken) {
|
|
authApi.me()
|
|
.then((res) => {
|
|
if (res.data.success) {
|
|
login(sessionToken, res.data.data.userInfo, res.data.data.site)
|
|
} else {
|
|
logout()
|
|
}
|
|
})
|
|
.catch(() => logout())
|
|
} else {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
return <>{children}</>
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<ConfigProvider locale={zhCN} theme={lightTheme}>
|
|
<AntApp>
|
|
<BrowserRouter>
|
|
<SessionRestore>
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/" element={<ProtectedRoute><Layout /></ProtectedRoute>}>
|
|
<Route index element={<Navigate to="/dashboard" />} />
|
|
<Route path="dashboard" element={<Dashboard />} />
|
|
<Route path="billing" element={<Billing />} />
|
|
<Route path="admin/sites" element={<SiteManagement />} />
|
|
</Route>
|
|
</Routes>
|
|
</SessionRestore>
|
|
</BrowserRouter>
|
|
</AntApp>
|
|
</ConfigProvider>
|
|
)
|
|
}
|