feat: allow public monitor route without login and add monitor page
This commit is contained in:
113
src/pages/MonitorPage.tsx
Normal file
113
src/pages/MonitorPage.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import api from '@/services/api'
|
||||
|
||||
interface ProviderRow {
|
||||
account_id: number
|
||||
provider: string
|
||||
label: string
|
||||
usage_pct: number | null
|
||||
status: string
|
||||
error?: string | null
|
||||
fetched_at?: string | null
|
||||
reset_at?: string | null
|
||||
}
|
||||
|
||||
interface ServerRow {
|
||||
server_id: number
|
||||
identifier: string
|
||||
display_name: string
|
||||
online: boolean
|
||||
openclaw_version?: string | null
|
||||
cpu_pct?: number | null
|
||||
mem_pct?: number | null
|
||||
disk_pct?: number | null
|
||||
swap_pct?: number | null
|
||||
agents: Array<{ id?: string; name?: string; status?: string }>
|
||||
last_seen_at?: string | null
|
||||
}
|
||||
|
||||
interface OverviewData {
|
||||
issues: {
|
||||
total_issues: number
|
||||
new_issues_24h: number
|
||||
processed_issues_24h: number
|
||||
computed_at: string
|
||||
}
|
||||
providers: ProviderRow[]
|
||||
servers: ServerRow[]
|
||||
generated_at: string
|
||||
}
|
||||
|
||||
export default function MonitorPage() {
|
||||
const [data, setData] = useState<OverviewData | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
const res = await api.get<OverviewData>('/monitor/public/overview')
|
||||
setData(res.data)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
load()
|
||||
const t = setInterval(load, 30000)
|
||||
return () => clearInterval(t)
|
||||
}, [])
|
||||
|
||||
if (loading) return <div className='loading'>Monitor loading...</div>
|
||||
if (!data) return <div className='loading'>Monitor load failed</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>📡 Monitor</h2>
|
||||
|
||||
<section>
|
||||
<h3>Issue 概览(24小时窗口)</h3>
|
||||
<ul>
|
||||
<li>所有项目总 Issue:{data.issues.total_issues}</li>
|
||||
<li>24小时新增:{data.issues.new_issues_24h}</li>
|
||||
<li>24小时已处理(resolved/closed):{data.issues.processed_issues_24h}</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Provider Usage</h3>
|
||||
{data.providers.length === 0 ? <p>暂无 provider 账号</p> : (
|
||||
<ul>
|
||||
{data.providers.map((p) => (
|
||||
<li key={p.account_id}>
|
||||
<strong>{p.provider}</strong> / {p.label} · status: {p.status}
|
||||
{p.usage_pct !== null ? : ''}
|
||||
{p.reset_at ? : ''}
|
||||
{p.error ? : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>服务器监测</h3>
|
||||
{data.servers.length === 0 ? <p>暂无监测服务器</p> : (
|
||||
<div>
|
||||
{data.servers.map((s) => (
|
||||
<div key={s.server_id} style={{ marginBottom: 12, padding: 12, border: '1px solid #ddd', borderRadius: 8 }}>
|
||||
<div>
|
||||
<strong>{s.display_name}</strong> ({s.identifier}) · {s.online ? '🟢 在线' : '🔴 离线'}
|
||||
</div>
|
||||
<div>
|
||||
CPU: {s.cpu_pct ?? '-'}% · MEM: {s.mem_pct ?? '-'}% · DISK: {s.disk_pct ?? '-'}% · SWAP: {s.swap_pct ?? '-'}%
|
||||
</div>
|
||||
<div>OpenClaw: {s.openclaw_version || '-'}</div>
|
||||
<div>Agents: {s.agents?.length || 0}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user