5adb3d621b
- 后端 Visit 模型(date+ip 唯一索引):POST /api/track 埋点(ClientIP upsert,pv+1),GET /api/admin/visits 每日 UV/PV 统计 - web 每会话上报一次访问(sessionStorage 防重),IP 由后端去重 - admin 访问统计页:侧边栏入口 + 今日/近N天 UV·PV 卡片 + 每日柱状趋势(自绘无依赖) + 7/30/90 天切换 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
import { Navigate, Outlet, Route, Routes } from 'react-router-dom'
|
|
import { getToken } from '@/api/client'
|
|
import { AdminLayout } from '@/components/admin-layout'
|
|
import LoginPage from '@/pages/login'
|
|
import DashboardPage from '@/pages/dashboard'
|
|
import PostsListPage from '@/pages/posts-list'
|
|
import PostEditPage from '@/pages/post-edit'
|
|
import VisitsPage from '@/pages/visits'
|
|
|
|
function RequireAuth() {
|
|
if (!getToken()) return <Navigate to="/login" replace />
|
|
return <Outlet />
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<Routes>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route element={<RequireAuth />}>
|
|
<Route element={<AdminLayout />}>
|
|
<Route index element={<DashboardPage />} />
|
|
<Route path="posts" element={<PostsListPage />} />
|
|
<Route path="posts/new" element={<PostEditPage />} />
|
|
<Route path="posts/:id" element={<PostEditPage />} />
|
|
<Route path="visits" element={<VisitsPage />} />
|
|
</Route>
|
|
</Route>
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
)
|
|
}
|