feat : ajout page monitoring
This commit is contained in:
163
pages/index.vue
Normal file
163
pages/index.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<NuxtLayout name="default">
|
||||
<template #sidebar>
|
||||
<div class="flex flex-col gap-">
|
||||
<DiagramStorage
|
||||
v-for="item in diagramItems"
|
||||
:key="item.key"
|
||||
:host-name="item.hostName"
|
||||
:status-color-class="item.statusColorClass"
|
||||
:chart-radius="chartRadius"
|
||||
:chart-circumference="chartCircumference"
|
||||
:chart-offset="item.chartOffset"
|
||||
:remaining-percent-text="item.remainingPercentText"
|
||||
:used-text="item.usedText"
|
||||
:total-text="item.totalText"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<p class="font-bold text-4xl my-6 mx-4">Écran de monitoring</p>
|
||||
<div class="flex">
|
||||
<StatusSite/>
|
||||
<Speedtest/>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Speedtest from "~/components/Speedtest.vue";
|
||||
|
||||
definePageMeta({layout: false})
|
||||
import {computed, onMounted, ref} from "vue"
|
||||
|
||||
type SourceKey = "remote" | "local"
|
||||
type DiskCommandResult = { ok: boolean; output: string }
|
||||
type DiskApiResponse = {
|
||||
remote?: string | DiskCommandResult
|
||||
local?: string | DiskCommandResult
|
||||
}
|
||||
|
||||
const rawResults = ref<Record<SourceKey, string>>({
|
||||
remote: "",
|
||||
local: ""
|
||||
})
|
||||
const loading = ref(false)
|
||||
const chartRadius = 52
|
||||
const chartCircumference = 2 * Math.PI * chartRadius
|
||||
|
||||
const getHostName = (output: string, fallback: string) => {
|
||||
const hostMatch = output.match(/Name:\s*(.+)/i)
|
||||
return hostMatch?.[1]?.trim() || fallback
|
||||
}
|
||||
|
||||
const getDiskValues = (output: string) => {
|
||||
if (!output || output.startsWith("Erreur:")) return null
|
||||
|
||||
const availableLine = output
|
||||
.split("\n")
|
||||
.find((line) => line.toLowerCase().includes("espace disponible"))
|
||||
const usageLine = output
|
||||
.split("\n")
|
||||
.find((line) => line.toLowerCase().includes("espace utilise / espace total"))
|
||||
|
||||
const availableRaw = availableLine?.match(/:\s*(\d+(?:[.,]\d+)?)\s*GB/i)?.[1]
|
||||
const usedRaw = usageLine?.match(/:\s*(\d+(?:[.,]\d+)?)\s*GB/i)?.[1]
|
||||
const totalRaw = usageLine?.match(/\/\s*(\d+(?:[.,]\d+)?)\s*GB/i)?.[1]
|
||||
|
||||
const availableGb = availableRaw ? Number.parseFloat(availableRaw.replace(",", ".")) : null
|
||||
const usedGb = usedRaw ? Number.parseFloat(usedRaw.replace(",", ".")) : null
|
||||
const totalGb = totalRaw ? Number.parseFloat(totalRaw.replace(",", ".")) : null
|
||||
|
||||
if (
|
||||
availableGb === null ||
|
||||
usedGb === null ||
|
||||
totalGb === null ||
|
||||
!Number.isFinite(availableGb) ||
|
||||
!Number.isFinite(usedGb) ||
|
||||
!Number.isFinite(totalGb) ||
|
||||
totalGb <= 0
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {availableGb, usedGb, totalGb}
|
||||
}
|
||||
|
||||
const getOutputText = (entry: unknown) => {
|
||||
if (typeof entry === "string") return entry
|
||||
if (entry && typeof entry === "object" && "output" in entry) {
|
||||
const output = (entry as DiskCommandResult).output
|
||||
return typeof output === "string" ? output : String(output)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const diagramItems = computed(() => {
|
||||
return [
|
||||
{ key: "remote" as const, fallbackHost: "Serveur distant", output: rawResults.value.remote },
|
||||
{ key: "local" as const, fallbackHost: "Machine locale", output: rawResults.value.local }
|
||||
].map((item) => {
|
||||
const diskValues = getDiskValues(item.output)
|
||||
const remainingPercent =
|
||||
diskValues === null
|
||||
? null
|
||||
: Math.max(
|
||||
0,
|
||||
Math.min(100, Math.round((diskValues.availableGb / diskValues.totalGb) * 100))
|
||||
)
|
||||
|
||||
const chartOffset = chartCircumference - ((remainingPercent ?? 0) / 100) * chartCircumference
|
||||
const statusColorClass =
|
||||
remainingPercent !== null && remainingPercent <= 30 ? "m-error" : "m-success"
|
||||
|
||||
return {
|
||||
key: item.key,
|
||||
hostName: getHostName(item.output, item.fallbackHost),
|
||||
statusColorClass,
|
||||
chartOffset,
|
||||
remainingPercentText:
|
||||
loading.value ? "..." : remainingPercent === null ? "--%" : `${remainingPercent}%`,
|
||||
usedText: loading.value ? "..." : diskValues ? `${diskValues.usedGb.toFixed(2)} GB` : "--",
|
||||
totalText: loading.value ? "..." : diskValues ? `${diskValues.totalGb.toFixed(2)} GB` : "--"
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const runScript = async () => {
|
||||
loading.value = true
|
||||
rawResults.value = {
|
||||
remote: "",
|
||||
local: ""
|
||||
}
|
||||
|
||||
try {
|
||||
const output = await $fetch<DiskApiResponse | string>("/api/disk")
|
||||
|
||||
if (typeof output === "string") {
|
||||
rawResults.value = {
|
||||
remote: output,
|
||||
local: "Erreur: sortie locale indisponible"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rawResults.value = {
|
||||
remote: getOutputText(output.remote),
|
||||
local: getOutputText(output.local)
|
||||
}
|
||||
} catch (error) {
|
||||
const message = `Erreur: ${error instanceof Error ? error.message : String(error)}`
|
||||
rawResults.value = {
|
||||
remote: message,
|
||||
local: message
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
runScript()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user