fix: arch-03 worker system metric

This commit is contained in:
2026-03-13 13:40:30 +01:00
parent c6d5843022
commit 6eddc11253
5 changed files with 302 additions and 108 deletions

View File

@@ -62,7 +62,7 @@ const metrics = computed(() => [
async function testDownload() {
const start = performance.now()
const res = await apiRequest('/api/download')
const res = await apiRequest('/api/speedtest')
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}

View File

@@ -3,27 +3,22 @@
<div class="card-header">
<div>
<h2 class="card-title">Historique systeme</h2>
<p class="card-copy">CPU, RAM et debit reseau avec cache journalier local</p>
<p class="card-copy">CPU et RAM avec cache journalier local</p>
</div>
<div class="controls">
<div class="toggle-group" role="radiogroup" aria-label="Metrique affichee">
<label
<div class="toggle-group" role="group" aria-label="Metriques affichees">
<button
v-for="option in options"
:key="option.value"
type="button"
class="toggle-pill"
:class="{ 'toggle-pill-active': selectedMetric === option.value }"
:class="{ 'toggle-pill-active': isMetricActive(option.value) }"
@click="toggleMetric(option.value)"
>
<input
v-model="selectedMetric"
type="radio"
name="system-metric"
class="sr-only"
:value="option.value"
>
<span class="toggle-dot" :style="{ backgroundColor: option.color }" />
<span>{{ option.label }}</span>
</label>
</button>
</div>
<div class="history-toolbar">
@@ -48,19 +43,11 @@
</div>
<div class="chart-shell">
<template v-if="loading && points.length === 0">
<template v-if="loading && visibleHistory.length === 0">
<div class="chart-skeleton animate-shimmer" />
</template>
<template v-else>
<div class="chart-meta">
<div>
<span class="meta-label">Actuel</span>
<strong class="meta-value" :style="{ color: activeOption.color }">{{ formattedCurrentValue }}</strong>
</div>
<div>
<span class="meta-label">Pic</span>
<strong class="meta-value">{{ formattedPeakValue }}</strong>
</div>
<div>
<span class="meta-label">Echelle</span>
<strong class="meta-value">{{ scaleLabel }}</strong>
@@ -69,6 +56,17 @@
<span class="meta-label">Periode</span>
<strong class="meta-value">{{ activeWindowLabel }}</strong>
</div>
<div
v-for="option in displayedOptions"
:key="option.value"
class="meta-metric"
>
<span class="meta-label">{{ option.label }}</span>
<strong class="meta-value" :style="{ color: option.color }">
{{ formatValue(currentMetricValue(option.value)) }}
</strong>
<span class="meta-subvalue">Pic {{ formatValue(peakMetricValue(option.value)) }}</span>
</div>
</div>
<svg
@@ -128,9 +126,11 @@
{{ tick.label }}
</text>
<polyline
:points="polylinePoints"
v-for="option in displayedOptions"
:key="option.value"
:points="polylinePoints(option.value)"
class="chart-line"
:style="{ stroke: activeOption.color }"
:style="{ stroke: option.color }"
/>
</svg>
</template>
@@ -142,15 +142,13 @@
import { computed, onMounted, ref, watch } from "vue"
import type { SystemMetrics } from "~/types/system"
type MetricKey = "cpu" | "ram" | "incoming" | "outgoing"
type WindowKey = "day" | "hour" | "30m" | "5m" | "1m" | "30s"
type MetricKey = "cpu" | "ram"
type WindowKey = "day" | "hour" | "5m" | "1m" | "30s"
type HistoryPoint = {
sampledAt: number
cpu: number
ram: number
incoming: number
outgoing: number
}
const HISTORY_STORAGE_KEY = "supervisor-system-history"
@@ -160,21 +158,18 @@ const props = defineProps<{
loading: boolean
}>()
const selectedMetric = ref<MetricKey>("ram")
const activeMetrics = ref<MetricKey[]>(["cpu", "ram"])
const selectedWindow = ref<WindowKey>("hour")
const history = ref<HistoryPoint[]>([])
const options: Array<{ value: MetricKey; label: string; color: string }> = [
{ value: "cpu", label: "CPU", color: "#5aa9ff" },
{ value: "ram", label: "RAM", color: "#31c48d" },
{ value: "incoming", label: "Entrant", color: "#f59e0b" },
{ value: "outgoing", label: "Sortant", color: "#ef4444" }
{ value: "ram", label: "RAM", color: "#31c48d" }
]
const windowOptions: Array<{ value: WindowKey; label: string; durationMs: number | null }> = [
{ value: "day", label: "Journee", durationMs: null },
{ value: "hour", label: "1 h", durationMs: 60 * 60 * 1000 },
{ value: "30m", label: "30 min", durationMs: 30 * 60 * 1000 },
{ value: "5m", label: "5 min", durationMs: 5 * 60 * 1000 },
{ value: "1m", label: "1 min", durationMs: 60 * 1000 },
{ value: "30s", label: "30 s", durationMs: 30 * 1000 }
@@ -216,9 +211,7 @@ const appendHistoryPoint = (metrics: SystemMetrics) => {
const nextPoint: HistoryPoint = {
sampledAt,
cpu: metrics.cpuPercent,
ram: metrics.memoryPercent,
incoming: metrics.incomingMbps,
outgoing: metrics.outgoingMbps
ram: metrics.memoryPercent
}
const previousPoint = history.value.at(-1)
@@ -261,9 +254,7 @@ onMounted(() => {
point &&
Number.isFinite(point.sampledAt) &&
Number.isFinite(point.cpu) &&
Number.isFinite(point.ram) &&
Number.isFinite(point.incoming) &&
Number.isFinite(point.outgoing)
Number.isFinite(point.ram)
)
})
)
@@ -285,14 +276,14 @@ watch(
{ immediate: true }
)
const activeOption = computed(() => {
return options.find((option) => option.value === selectedMetric.value) || options[0]
})
const activeWindow = computed(() => {
return windowOptions.find((option) => option.value === selectedWindow.value) || windowOptions[0]
})
const displayedOptions = computed(() => {
return options.filter((option) => activeMetrics.value.includes(option.value))
})
const visibleHistory = computed(() => {
if (activeWindow.value.durationMs === null) {
return history.value
@@ -302,43 +293,35 @@ const visibleHistory = computed(() => {
return history.value.filter((point) => point.sampledAt >= minTimestamp)
})
const points = computed(() => visibleHistory.value.map((point) => point[selectedMetric.value]))
const scaleMax = computed(() => 100)
const peakValue = computed(() => {
return points.value.reduce((max, value) => Math.max(max, value), 0)
})
const scaleMax = computed(() => {
if (selectedMetric.value === "cpu" || selectedMetric.value === "ram") {
return 100
}
return Math.max(1, Math.ceil(peakValue.value))
})
const formatValue = (value: number, metric: MetricKey) => {
if (metric === "cpu" || metric === "ram") {
return `${Math.round(value)}%`
}
return `${value.toFixed(2)} Mbps`
}
const formattedCurrentValue = computed(() => {
const currentValue = points.value.at(-1) ?? 0
return formatValue(currentValue, selectedMetric.value)
})
const formattedPeakValue = computed(() => {
return formatValue(peakValue.value, selectedMetric.value)
})
const formatValue = (value: number) => `${Math.round(value)}%`
const scaleLabel = computed(() => {
return formatValue(scaleMax.value, selectedMetric.value)
return formatValue(scaleMax.value)
})
const activeWindowLabel = computed(() => activeWindow.value.label)
const isMetricActive = (metric: MetricKey) => activeMetrics.value.includes(metric)
const toggleMetric = (metric: MetricKey) => {
if (isMetricActive(metric)) {
activeMetrics.value = activeMetrics.value.filter((value) => value !== metric)
return
}
activeMetrics.value = [...activeMetrics.value, metric]
}
const currentMetricValue = (metric: MetricKey) => {
return visibleHistory.value.at(-1)?.[metric] ?? 0
}
const peakMetricValue = (metric: MetricKey) => {
return visibleHistory.value.reduce((max, point) => Math.max(max, point[metric]), 0)
}
const chartLeft = 72
const chartRight = 936
const chartTop = 24
@@ -356,7 +339,7 @@ const yAxisTicks = computed(() => {
return {
y,
label: formatValue(value, selectedMetric.value)
label: formatValue(value)
}
})
})
@@ -406,27 +389,29 @@ const xAxisTicks = computed(() => {
})
})
const polylinePoints = computed(() => {
if (points.value.length === 0) {
const polylinePoints = (metric: MetricKey) => {
const points = visibleHistory.value.map((point) => point[metric])
if (points.length === 0) {
return `${chartLeft},${chartBottom}`
}
if (points.value.length === 1) {
const normalizedValue = points.value[0] / scaleMax.value
if (points.length === 1) {
const normalizedValue = points[0] / scaleMax.value
const y = chartBottom - normalizedValue * chartHeight
return `${chartLeft},${y} ${chartRight},${y}`
}
return points.value
return points
.map((value, index) => {
const x = chartLeft + (index / (points.value.length - 1)) * chartWidth
const x = chartLeft + (index / (points.length - 1)) * chartWidth
const normalizedValue = scaleMax.value > 0 ? value / scaleMax.value : 0
const y = chartBottom - normalizedValue * chartHeight
return `${x},${Math.max(chartTop, Math.min(chartBottom, y))}`
})
.join(" ")
})
}
</script>
<style scoped>
@@ -492,6 +477,7 @@ const polylinePoints = computed(() => {
letter-spacing: 0.12em;
color: rgb(var(--m-muted));
cursor: pointer;
appearance: none;
transition: border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
@@ -570,6 +556,12 @@ const polylinePoints = computed(() => {
margin-bottom: 1rem;
}
.meta-metric {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.meta-label {
display: block;
margin-bottom: 0.25rem;
@@ -582,11 +574,19 @@ const polylinePoints = computed(() => {
.meta-value {
font-family: var(--font-display);
font-size: 1.1rem;
font-size: 1.35rem;
font-weight: 700;
color: rgb(var(--m-text));
}
.meta-subvalue {
font-family: var(--font-mono);
font-size: 0.7rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgb(var(--m-muted));
}
.chart-svg {
width: 100%;
height: 320px;