feat(ui): Monitoring renders from cached resources (B4)
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m7s
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m7s
monitoring.current/history/alerts/alert-rules become useCachedResource entries: revisiting the page paints the last snapshot, chart, and alert list instantly and the 5s poll revalidates behind them (refreshes dedup in the store; errors keep last-known values instead of blanking). Alert-rule toggles and acknowledgements refresh their entries after the mutation as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
529c7fe25d
commit
43e50e669e
@ -218,6 +218,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useCachedResource } from '@/composables/useCachedResource'
|
||||
import { useHomeStatusStore } from '@/stores/homeStatus'
|
||||
import BackButton from '@/components/BackButton.vue'
|
||||
import LineChart from '@/components/LineChart.vue'
|
||||
@ -291,11 +292,51 @@ const backTarget = computed(() => (cameFromHome.value ? '/dashboard' : '/dashboa
|
||||
const backLabel = computed(() => (cameFromHome.value ? t('common.back') : 'Web5'))
|
||||
const homeStatus = useHomeStatusStore()
|
||||
|
||||
const current = ref<MetricSnapshot | null>(null)
|
||||
const history = ref<MetricSnapshot[]>([])
|
||||
const containers = ref<ContainerMetrics[]>([])
|
||||
const alerts = ref<FiredAlert[]>([])
|
||||
const alertRules = ref<AlertRule[]>([])
|
||||
// Cached: revisits paint the last snapshot/chart/alerts instantly and the 5s
|
||||
// poll revalidates behind them; errors keep the last-known values.
|
||||
const currentRes = useCachedResource<MetricSnapshot>({
|
||||
key: 'monitoring.current',
|
||||
fetcher: async (signal) => {
|
||||
const data = await rpcClient.call<MetricSnapshot | { status: string }>({
|
||||
method: 'monitoring.current', signal, dedup: true, maxRetries: 1,
|
||||
})
|
||||
if (!data || !('system' in data)) throw new Error('metrics not ready')
|
||||
return data
|
||||
},
|
||||
})
|
||||
const historyRes = useCachedResource<MetricSnapshot[]>({
|
||||
key: 'monitoring.history.minute60',
|
||||
fetcher: async (signal) => {
|
||||
const data = await rpcClient.call<HistoryResponse>({
|
||||
method: 'monitoring.history', params: { resolution: 'minute', count: 60 },
|
||||
signal, dedup: true, maxRetries: 1,
|
||||
})
|
||||
return data?.data ?? []
|
||||
},
|
||||
})
|
||||
const alertsRes = useCachedResource<FiredAlert[]>({
|
||||
key: 'monitoring.alerts',
|
||||
fetcher: async (signal) => {
|
||||
const data = await rpcClient.call<{ alerts: FiredAlert[] }>({
|
||||
method: 'monitoring.alerts', params: { count: 50 }, signal, dedup: true, maxRetries: 1,
|
||||
})
|
||||
return (data?.alerts ?? []).reverse()
|
||||
},
|
||||
})
|
||||
const alertRulesRes = useCachedResource<AlertRule[]>({
|
||||
key: 'monitoring.alert-rules',
|
||||
fetcher: async (signal) => {
|
||||
const data = await rpcClient.call<{ rules: AlertRule[] }>({
|
||||
method: 'monitoring.alert-rules', signal, dedup: true, maxRetries: 1,
|
||||
})
|
||||
return data?.rules ?? []
|
||||
},
|
||||
})
|
||||
const current = computed(() => currentRes.data.value)
|
||||
const history = computed(() => historyRes.data.value ?? [])
|
||||
const containers = computed<ContainerMetrics[]>(() => currentRes.data.value?.containers ?? [])
|
||||
const alerts = computed(() => alertsRes.data.value ?? [])
|
||||
const alertRules = computed(() => alertRulesRes.data.value ?? [])
|
||||
const showAlertConfig = ref(false)
|
||||
const chartWidth = ref(380)
|
||||
let pollTimer: ReturnType<typeof setInterval> | null = null
|
||||
@ -464,66 +505,10 @@ async function exportMetrics(format: 'csv' | 'json') {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCurrent() {
|
||||
try {
|
||||
await homeStatus.refreshSystemStats()
|
||||
const data = await rpcClient.call<MetricSnapshot | { status: string }>({
|
||||
method: 'monitoring.current',
|
||||
})
|
||||
if (data && 'system' in data) {
|
||||
current.value = data
|
||||
containers.value = data.containers ?? []
|
||||
}
|
||||
} catch {
|
||||
// Silently retry on next poll
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchHistory() {
|
||||
try {
|
||||
const data = await rpcClient.call<HistoryResponse>({
|
||||
method: 'monitoring.history',
|
||||
params: { resolution: 'minute', count: 60 },
|
||||
})
|
||||
if (data?.data) {
|
||||
history.value = data.data
|
||||
}
|
||||
} catch {
|
||||
// Silently retry on next poll
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAlerts() {
|
||||
try {
|
||||
const data = await rpcClient.call<{ alerts: FiredAlert[] }>({
|
||||
method: 'monitoring.alerts',
|
||||
params: { count: 50 },
|
||||
})
|
||||
if (data?.alerts) {
|
||||
alerts.value = data.alerts.reverse()
|
||||
}
|
||||
} catch {
|
||||
// Silently retry on next poll
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAlertRules() {
|
||||
try {
|
||||
const data = await rpcClient.call<{ rules: AlertRule[] }>({
|
||||
method: 'monitoring.alert-rules',
|
||||
})
|
||||
if (data?.rules) {
|
||||
alertRules.value = data.rules
|
||||
}
|
||||
} catch {
|
||||
// Non-critical
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleAlertRule(kind: string, enabled: boolean) {
|
||||
try {
|
||||
await rpcClient.call({ method: 'monitoring.configure-alert', params: { kind, enabled } })
|
||||
await fetchAlertRules()
|
||||
await alertRulesRes.refresh()
|
||||
} catch {
|
||||
// Non-critical
|
||||
}
|
||||
@ -534,7 +519,7 @@ async function updateThreshold(kind: string, value: string) {
|
||||
if (isNaN(threshold) || threshold <= 0) return
|
||||
try {
|
||||
await rpcClient.call({ method: 'monitoring.configure-alert', params: { kind, threshold } })
|
||||
await fetchAlertRules()
|
||||
await alertRulesRes.refresh()
|
||||
} catch {
|
||||
// Non-critical
|
||||
}
|
||||
@ -543,7 +528,7 @@ async function updateThreshold(kind: string, value: string) {
|
||||
async function acknowledgeAlert(id: string) {
|
||||
try {
|
||||
await rpcClient.call({ method: 'monitoring.acknowledge-alert', params: { id } })
|
||||
await fetchAlerts()
|
||||
await alertsRes.refresh()
|
||||
} catch {
|
||||
// Non-critical
|
||||
}
|
||||
@ -556,18 +541,18 @@ function updateChartWidth() {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
onMounted(() => {
|
||||
updateChartWidth()
|
||||
window.addEventListener('resize', updateChartWidth)
|
||||
|
||||
await Promise.all([fetchCurrent(), fetchHistory(), fetchAlerts(), fetchAlertRules()])
|
||||
|
||||
pollTimer = setInterval(async () => {
|
||||
try {
|
||||
await Promise.all([fetchCurrent(), fetchHistory(), fetchAlerts()])
|
||||
} catch {
|
||||
// Background poll — ignore transient errors
|
||||
}
|
||||
// The cached resources fetch themselves on first use; the poll keeps the
|
||||
// live view fresh (refreshes dedup in the store).
|
||||
void homeStatus.refreshSystemStats()
|
||||
pollTimer = setInterval(() => {
|
||||
void homeStatus.refreshSystemStats()
|
||||
void currentRes.refresh()
|
||||
void historyRes.refresh()
|
||||
void alertsRes.refresh()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user