feat: 仪表盘路径改为 /dashboard + Forbidden 权限修复
This commit is contained in:
parent
5cd300bee8
commit
de0116ddc8
|
|
@ -77,7 +77,7 @@ export async function GET(request: NextRequest) {
|
||||||
payload: { username: userinfo.preferred_username, displayName: userinfo.name, role: user.role },
|
payload: { username: userinfo.preferred_username, displayName: userinfo.name, role: user.role },
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = NextResponse.redirect(new URL('/', baseUrl))
|
const response = NextResponse.redirect(new URL('/dashboard', baseUrl))
|
||||||
|
|
||||||
// 签发 tlyq_session cookie
|
// 签发 tlyq_session cookie
|
||||||
response.cookies.set('tlyq_session', token, {
|
response.cookies.set('tlyq_session', token, {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
'use client'
|
||||||
|
// src/app/dashboard/page.tsx — 仪表盘首页(四层结构)
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function DashboardPage() {
|
||||||
|
const [data, setData] = useState<{ services: Array<Record<string,unknown>>; counts: Record<string,number> }>({ services: [], counts: { normal: 0, abnormal: 0, unknown: 0 } })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/status').then(r => r.json()).then(setData)
|
||||||
|
const timer = setInterval(() => fetch('/api/status').then(r => r.json()).then(setData), 30_000)
|
||||||
|
return () => clearInterval(timer)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const { services, counts } = data
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-8">
|
||||||
|
{/* 标题 */}
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-semibold font-[family-name:var(--font-display)]">服务状态监控 <span className="text-slate-400 dark:text-slate-500 font-normal">· {services.length} 个服务</span></h1>
|
||||||
|
<p className="text-sm text-slate-400 dark:text-slate-500 mt-1">实时健康检查 · 30 秒轮询</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 状态统计条 */}
|
||||||
|
<div className="flex gap-px bg-slate-200 dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">正常</span>
|
||||||
|
<span className="text-2xl font-semibold text-emerald-600 font-[family-name:var(--font-display)]">{counts.normal}</span>
|
||||||
|
<span className="text-xs text-slate-400">共 {services.length} 个监控服务</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">异常</span>
|
||||||
|
<span className="text-2xl font-semibold text-red-600 font-[family-name:var(--font-display)]">{counts.abnormal}</span>
|
||||||
|
<span className="text-xs text-slate-400">{services.filter(s => s.current_status === 'abnormal').map(s => s.name).join(', ') || '—'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">未知</span>
|
||||||
|
<span className="text-2xl font-semibold text-amber-600 font-[family-name:var(--font-display)]">{counts.unknown}</span>
|
||||||
|
<span className="text-xs text-slate-400">{counts.unknown > 0 ? '待首次检查' : '全部已检查'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1 text-right">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">最后检查</span>
|
||||||
|
<span className="text-base font-medium text-slate-600 dark:text-slate-300">刚刚</span>
|
||||||
|
<span className="text-xs text-slate-400">自动刷新 30s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* KPI 统计条 */}
|
||||||
|
<div className="flex gap-px bg-slate-200 dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">本月可用率</span>
|
||||||
|
<span className="text-2xl font-semibold text-emerald-600 font-[family-name:var(--font-display)]">—</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
||||||
|
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">活跃渠道</span>
|
||||||
|
<span className="text-2xl font-semibold font-[family-name:var(--font-display)]">—</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 分割线 */}
|
||||||
|
<hr className="border-t border-slate-200 dark:border-slate-800" />
|
||||||
|
|
||||||
|
{/* 服务卡片网格 */}
|
||||||
|
<div className="grid grid-cols-4 gap-5">
|
||||||
|
{services.length === 0 && (
|
||||||
|
<div className="col-span-4 text-center py-12 text-slate-400 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||||
|
请先在「服务管理」中添加被监控服务
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{services.map((s) => {
|
||||||
|
const isAbnormal = s.current_status === 'abnormal'
|
||||||
|
return (
|
||||||
|
<div key={String(s.id)} className={`rounded-xl p-5 border transition-all ${isAbnormal ? 'bg-red-50 dark:bg-red-500/10 border-red-600 dark:border-red-400' : 'bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:shadow-sm'}`}>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="flex items-center gap-2 text-sm font-semibold">
|
||||||
|
<span className={`w-2.5 h-2.5 rounded-full ${isAbnormal ? 'bg-red-600 shadow-[0_0_8px_var(--danger)]' : 'bg-emerald-600'}`} />
|
||||||
|
{String(s.name)}
|
||||||
|
</span>
|
||||||
|
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${s.alert_level === 'critical' ? 'bg-red-100 text-red-700 dark:bg-red-500/10 dark:text-red-400' : 'bg-green-100 text-green-700 dark:bg-green-500/10 dark:text-green-400'}`}>
|
||||||
|
{String(s.alert_level)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{isAbnormal
|
||||||
|
? <div className="text-lg font-semibold font-mono text-red-600 dark:text-red-400">异常</div>
|
||||||
|
: <div className="text-xs text-slate-400">运行中</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
import LoginPage from '@shared/ui/login-page'
|
import LoginPage from '@shared/ui/login-page'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return <LoginPage siteName="告警监控中心" />
|
return <LoginPage siteName="告警监控中心" redirectPath="/dashboard" />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,5 @@
|
||||||
'use client'
|
import { redirect } from 'next/navigation'
|
||||||
// src/app/page.tsx — 仪表盘首页(四层结构)
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const [data, setData] = useState<{ services: Array<Record<string,unknown>>; counts: Record<string,number> }>({ services: [], counts: { normal: 0, abnormal: 0, unknown: 0 } })
|
redirect('/dashboard')
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/api/status').then(r => r.json()).then(setData)
|
|
||||||
const timer = setInterval(() => fetch('/api/status').then(r => r.json()).then(setData), 30_000)
|
|
||||||
return () => clearInterval(timer)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const { services, counts } = data
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-8">
|
|
||||||
{/* 标题 */}
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-semibold font-[family-name:var(--font-display)]">服务状态监控 <span className="text-slate-400 dark:text-slate-500 font-normal">· {services.length} 个服务</span></h1>
|
|
||||||
<p className="text-sm text-slate-400 dark:text-slate-500 mt-1">实时健康检查 · 30 秒轮询</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 状态统计条 */}
|
|
||||||
<div className="flex gap-px bg-slate-200 dark:bg-slate-800 rounded-xl overflow-hidden">
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">正常</span>
|
|
||||||
<span className="text-2xl font-semibold text-emerald-600 font-[family-name:var(--font-display)]">{counts.normal}</span>
|
|
||||||
<span className="text-xs text-slate-400">共 {services.length} 个监控服务</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">异常</span>
|
|
||||||
<span className="text-2xl font-semibold text-red-600 font-[family-name:var(--font-display)]">{counts.abnormal}</span>
|
|
||||||
<span className="text-xs text-slate-400">{services.filter(s => s.current_status === 'abnormal').map(s => s.name).join(', ') || '—'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">未知</span>
|
|
||||||
<span className="text-2xl font-semibold text-amber-600 font-[family-name:var(--font-display)]">{counts.unknown}</span>
|
|
||||||
<span className="text-xs text-slate-400">{counts.unknown > 0 ? '待首次检查' : '全部已检查'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1 text-right">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">最后检查</span>
|
|
||||||
<span className="text-base font-medium text-slate-600 dark:text-slate-300">刚刚</span>
|
|
||||||
<span className="text-xs text-slate-400">自动刷新 30s</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* KPI 统计条 */}
|
|
||||||
<div className="flex gap-px bg-slate-200 dark:bg-slate-800 rounded-xl overflow-hidden">
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">本月可用率</span>
|
|
||||||
<span className="text-2xl font-semibold text-emerald-600 font-[family-name:var(--font-display)]">—</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 bg-white dark:bg-slate-900 px-6 py-4 flex flex-col gap-1">
|
|
||||||
<span className="text-xs font-medium text-slate-400 uppercase tracking-widest">活跃渠道</span>
|
|
||||||
<span className="text-2xl font-semibold font-[family-name:var(--font-display)]">—</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 分割线 */}
|
|
||||||
<hr className="border-t border-slate-200 dark:border-slate-800" />
|
|
||||||
|
|
||||||
{/* 服务卡片网格 */}
|
|
||||||
<div className="grid grid-cols-4 gap-5">
|
|
||||||
{services.length === 0 && (
|
|
||||||
<div className="col-span-4 text-center py-12 text-slate-400 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800">
|
|
||||||
请先在「服务管理」中添加被监控服务
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{services.map((s) => {
|
|
||||||
const isAbnormal = s.current_status === 'abnormal'
|
|
||||||
return (
|
|
||||||
<div key={String(s.id)} className={`rounded-xl p-5 border transition-all ${isAbnormal ? 'bg-red-50 dark:bg-red-500/10 border-red-600 dark:border-red-400' : 'bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:shadow-sm'}`}>
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="flex items-center gap-2 text-sm font-semibold">
|
|
||||||
<span className={`w-2.5 h-2.5 rounded-full ${isAbnormal ? 'bg-red-600 shadow-[0_0_8px_var(--danger)]' : 'bg-emerald-600'}`} />
|
|
||||||
{String(s.name)}
|
|
||||||
</span>
|
|
||||||
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${s.alert_level === 'critical' ? 'bg-red-100 text-red-700 dark:bg-red-500/10 dark:text-red-400' : 'bg-green-100 text-green-700 dark:bg-green-500/10 dark:text-green-400'}`}>
|
|
||||||
{String(s.alert_level)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{isAbnormal
|
|
||||||
? <div className="text-lg font-semibold font-mono text-red-600 dark:text-red-400">异常</div>
|
|
||||||
: <div className="text-xs text-slate-400">运行中</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default function Sidebar() {
|
||||||
const sections: NavSection[] = [
|
const sections: NavSection[] = [
|
||||||
{
|
{
|
||||||
items: [
|
items: [
|
||||||
{ label: '仪表盘', href: '/', icon: LayoutDashboard },
|
{ label: '仪表盘', href: '/dashboard', icon: LayoutDashboard },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +49,7 @@ export default function Sidebar() {
|
||||||
<aside className="fixed left-0 top-0 bottom-0 w-60 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800 z-[200] flex flex-col">
|
<aside className="fixed left-0 top-0 bottom-0 w-60 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800 z-[200] flex flex-col">
|
||||||
{/* 品牌区 */}
|
{/* 品牌区 */}
|
||||||
<div className="h-14 flex items-center px-6 border-b border-slate-200 dark:border-slate-800 shrink-0">
|
<div className="h-14 flex items-center px-6 border-b border-slate-200 dark:border-slate-800 shrink-0">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/dashboard" className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-indigo-600 flex items-center justify-center text-white font-bold text-sm">M</div>
|
<div className="w-8 h-8 rounded-lg bg-indigo-600 flex items-center justify-center text-white font-bold text-sm">M</div>
|
||||||
<span className="font-semibold text-slate-900 dark:text-white">monitor-ai</span>
|
<span className="font-semibold text-slate-900 dark:text-white">monitor-ai</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue