feat(monitoring): 監控工具區塊 — Grafana/Prometheus/SigNoz/Gitea 狀態
- 新增 GET /api/v1/monitoring/status,asyncio.gather 並行探測四工具 - 前端 MonitoringTools 元件,60s 輪詢顯示狀態/版本/統計 - 新增 monitoringTools i18n key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,10 +8,12 @@
|
||||
* 統帥鐵律: 使用真實數據 Hook,禁止假數據!
|
||||
*
|
||||
* @updated 2026-04-02 Claude Code — Metrics Strip 7指標視覺強化
|
||||
* @updated 2026-04-03 Claude Code — 監控工具區塊 (Grafana/Prometheus/SigNoz/Gitea)
|
||||
* 串接: incidents(count/P0/MTTR/autoRemediation) + dashboard(serviceHealth/pendingApprovals/podHealth)
|
||||
*/
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useGlobalPulseMetrics } from '@/hooks/useGlobalPulseMetrics'
|
||||
import { useIncidents } from '@/hooks/useIncidents'
|
||||
import { useHosts, useDashboardStore } from '@/stores/dashboard.store'
|
||||
@@ -20,6 +22,8 @@ import { OpenClawPanel } from '@/components/ai/openclaw-panel'
|
||||
import { HostGrid, type HostInfo } from '@/components/infra/host-grid'
|
||||
import { AppLayout } from '@/components/layout'
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
// =============================================================================
|
||||
@@ -55,6 +59,96 @@ function MiniSparkline({ values, color }: { values: number[]; color: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Monitoring Tools Component
|
||||
// =============================================================================
|
||||
|
||||
interface MonitoringTool {
|
||||
name: string
|
||||
status: string
|
||||
version: string | null
|
||||
stats: string | null
|
||||
description: string
|
||||
firing_count?: number
|
||||
checked_at: string
|
||||
}
|
||||
|
||||
function MonitoringTools() {
|
||||
const [tools, setTools] = useState<MonitoringTool[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const load = () => {
|
||||
fetch(`${API_BASE}/api/v1/monitoring/status`)
|
||||
.then(r => r.json())
|
||||
.then(d => { setTools(d.tools ?? []); setLoading(false) })
|
||||
.catch(() => setLoading(false))
|
||||
}
|
||||
load()
|
||||
const t = setInterval(load, 60000)
|
||||
return () => clearInterval(t)
|
||||
}, [])
|
||||
|
||||
const TOOL_ICONS: Record<string, string> = {
|
||||
Grafana: '📊',
|
||||
Prometheus: '🔥',
|
||||
SigNoz: '🔭',
|
||||
Gitea: '🐙',
|
||||
}
|
||||
|
||||
if (loading) return (
|
||||
<div style={{ padding: '12px 14px', fontSize: 12, color: '#87867f', fontFamily: 'var(--font-body), monospace' }}>
|
||||
載入中...
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{tools.map((tool, i) => {
|
||||
const isUp = tool.status === 'up'
|
||||
const hasFiring = (tool.firing_count ?? 0) > 0
|
||||
return (
|
||||
<div key={tool.name} style={{
|
||||
padding: '10px 14px',
|
||||
borderBottom: i < tools.length - 1 ? '0.5px solid #f0ede4' : 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
}}>
|
||||
<div style={{ fontSize: 18, flexShrink: 0, width: 24, textAlign: 'center' }}>{TOOL_ICONS[tool.name] ?? '⚙️'}</div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 2 }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 700, color: '#141413', fontFamily: 'var(--font-body), monospace' }}>{tool.name}</span>
|
||||
<span style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 3,
|
||||
fontSize: 10, fontWeight: 600,
|
||||
color: isUp ? (hasFiring ? '#F59E0B' : '#22C55E') : '#cc2200',
|
||||
background: isUp ? (hasFiring ? 'rgba(245,158,11,0.08)' : 'rgba(34,197,94,0.08)') : 'rgba(204,34,0,0.08)',
|
||||
border: `0.5px solid ${isUp ? (hasFiring ? 'rgba(245,158,11,0.25)' : 'rgba(34,197,94,0.25)') : 'rgba(204,34,0,0.25)'}`,
|
||||
borderRadius: 4, padding: '1px 5px',
|
||||
}}>
|
||||
<span style={{ width: 4, height: 4, borderRadius: '50%', background: 'currentColor', display: 'inline-block' }} />
|
||||
{isUp ? (hasFiring ? `${tool.firing_count} 觸發` : '正常') : '離線'}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: '#87867f', fontFamily: 'var(--font-body), monospace' }}>
|
||||
{tool.description}
|
||||
{tool.version && <span style={{ color: '#c0bdb4' }}> · v{tool.version}</span>}
|
||||
</div>
|
||||
{tool.stats && (
|
||||
<div style={{ fontSize: 11, color: '#a0a09a', fontFamily: 'var(--font-body), monospace', marginTop: 1 }}>{tool.stats}</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: '#c0bdb4', fontFamily: 'var(--font-body), monospace', flexShrink: 0, textAlign: 'right' }}>
|
||||
{new Date(tool.checked_at).toLocaleTimeString('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit' })}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Main Page
|
||||
// =============================================================================
|
||||
@@ -414,6 +508,29 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
}))} />
|
||||
</div>
|
||||
|
||||
{/* 監控工具 */}
|
||||
<div style={{
|
||||
background: '#fff',
|
||||
border: '0.5px solid #e0ddd4',
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 1px 4px rgba(0,0,0,0.05)',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<div style={{
|
||||
padding: '10px 14px',
|
||||
borderBottom: '0.5px solid #e0ddd4',
|
||||
fontSize: 14, fontWeight: 700, color: '#141413',
|
||||
letterSpacing: '0.5px',
|
||||
fontFamily: 'var(--font-body), monospace', background: '#faf9f3',
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
}}>
|
||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#d97757', flexShrink: 0 }} />
|
||||
{tDashboard('monitoringTools')}
|
||||
</div>
|
||||
<MonitoringTools />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user