Frontend: Removed the server metrics widget
This commit is contained in:
parent
1366adcebf
commit
b2a55f1d6d
|
@ -61,10 +61,6 @@ export default function (app: App) {
|
||||||
"MkwSlideshow",
|
"MkwSlideshow",
|
||||||
defineAsyncComponent(() => import("./slideshow.vue"))
|
defineAsyncComponent(() => import("./slideshow.vue"))
|
||||||
);
|
);
|
||||||
app.component(
|
|
||||||
"MkwServerMetric",
|
|
||||||
defineAsyncComponent(() => import("./server-metric/index.vue"))
|
|
||||||
);
|
|
||||||
app.component(
|
app.component(
|
||||||
"MkwOnlineUsers",
|
"MkwOnlineUsers",
|
||||||
defineAsyncComponent(() => import("./online-users.vue"))
|
defineAsyncComponent(() => import("./online-users.vue"))
|
||||||
|
@ -108,7 +104,6 @@ export const widgets = [
|
||||||
"federation",
|
"federation",
|
||||||
"postForm",
|
"postForm",
|
||||||
"slideshow",
|
"slideshow",
|
||||||
"serverMetric",
|
|
||||||
"serverInfo",
|
"serverInfo",
|
||||||
"onlineUsers",
|
"onlineUsers",
|
||||||
"jobQueue",
|
"jobQueue",
|
||||||
|
|
|
@ -1,193 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="lcfyofjk">
|
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
|
||||||
<defs>
|
|
||||||
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
|
|
||||||
<stop offset="0%" stop-color="hsl(189, 43%, 73%)"></stop>
|
|
||||||
<stop offset="100%" stop-color="hsl(343, 76%, 68%)"></stop>
|
|
||||||
</linearGradient>
|
|
||||||
<mask
|
|
||||||
:id="cpuMaskId"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
:width="viewBoxX"
|
|
||||||
:height="viewBoxY"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
:points="cpuPolygonPoints"
|
|
||||||
fill="#fff"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
:points="cpuPolylinePoints"
|
|
||||||
fill="none"
|
|
||||||
stroke="#fff"
|
|
||||||
stroke-width="1"
|
|
||||||
/>
|
|
||||||
<circle :cx="cpuHeadX" :cy="cpuHeadY" r="1.5" fill="#fff" />
|
|
||||||
</mask>
|
|
||||||
</defs>
|
|
||||||
<rect
|
|
||||||
x="-2"
|
|
||||||
y="-2"
|
|
||||||
:width="viewBoxX + 4"
|
|
||||||
:height="viewBoxY + 4"
|
|
||||||
:style="`stroke: none; fill: url(#${cpuGradientId}); mask: url(#${cpuMaskId})`"
|
|
||||||
/>
|
|
||||||
<text x="1" y="5">
|
|
||||||
CPU
|
|
||||||
<tspan>{{ cpuP }}%</tspan>
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
|
||||||
<defs>
|
|
||||||
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
|
|
||||||
<stop offset="0%" stop-color="hsl(189, 43%, 73%)"></stop>
|
|
||||||
<stop offset="100%" stop-color="hsl(343, 76%, 68%)"></stop>
|
|
||||||
</linearGradient>
|
|
||||||
<mask
|
|
||||||
:id="memMaskId"
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
:width="viewBoxX"
|
|
||||||
:height="viewBoxY"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
:points="memPolygonPoints"
|
|
||||||
fill="#fff"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
:points="memPolylinePoints"
|
|
||||||
fill="none"
|
|
||||||
stroke="#fff"
|
|
||||||
stroke-width="1"
|
|
||||||
/>
|
|
||||||
<circle :cx="memHeadX" :cy="memHeadY" r="1.5" fill="#fff" />
|
|
||||||
</mask>
|
|
||||||
</defs>
|
|
||||||
<rect
|
|
||||||
x="-2"
|
|
||||||
y="-2"
|
|
||||||
:width="viewBoxX + 4"
|
|
||||||
:height="viewBoxY + 4"
|
|
||||||
:style="`stroke: none; fill: url(#${memGradientId}); mask: url(#${memMaskId})`"
|
|
||||||
/>
|
|
||||||
<text x="1" y="5">
|
|
||||||
MEM
|
|
||||||
<tspan>{{ memP }}%</tspan>
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
connection: any;
|
|
||||||
meta: any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let viewBoxX: number = $ref(50);
|
|
||||||
let viewBoxY: number = $ref(30);
|
|
||||||
let stats: any[] = $ref([]);
|
|
||||||
const cpuGradientId = uuid();
|
|
||||||
const cpuMaskId = uuid();
|
|
||||||
const memGradientId = uuid();
|
|
||||||
const memMaskId = uuid();
|
|
||||||
let cpuPolylinePoints: string = $ref("");
|
|
||||||
let memPolylinePoints: string = $ref("");
|
|
||||||
let cpuPolygonPoints: string = $ref("");
|
|
||||||
let memPolygonPoints: string = $ref("");
|
|
||||||
let cpuHeadX: any = $ref(null);
|
|
||||||
let cpuHeadY: any = $ref(null);
|
|
||||||
let memHeadX: any = $ref(null);
|
|
||||||
let memHeadY: any = $ref(null);
|
|
||||||
let cpuP: string = $ref("");
|
|
||||||
let memP: string = $ref("");
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
props.connection.on("stats", onStats);
|
|
||||||
props.connection.on("statsLog", onStatsLog);
|
|
||||||
props.connection.send("requestLog", {
|
|
||||||
id: Math.random().toString().substr(2, 8),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
props.connection.off("stats", onStats);
|
|
||||||
props.connection.off("statsLog", onStatsLog);
|
|
||||||
});
|
|
||||||
|
|
||||||
function onStats(connStats) {
|
|
||||||
stats.push(connStats);
|
|
||||||
if (stats.length > 50) stats.shift();
|
|
||||||
|
|
||||||
let cpuPolylinePointsStats = stats.map((s, i) => [
|
|
||||||
viewBoxX - (stats.length - 1 - i),
|
|
||||||
(1 - s.cpu) * viewBoxY,
|
|
||||||
]);
|
|
||||||
let memPolylinePointsStats = stats.map((s, i) => [
|
|
||||||
viewBoxX - (stats.length - 1 - i),
|
|
||||||
(1 - s.mem.active / props.meta.mem.total) * viewBoxY,
|
|
||||||
]);
|
|
||||||
cpuPolylinePoints = cpuPolylinePointsStats
|
|
||||||
.map((xy) => `${xy[0]},${xy[1]}`)
|
|
||||||
.join(" ");
|
|
||||||
memPolylinePoints = memPolylinePointsStats
|
|
||||||
.map((xy) => `${xy[0]},${xy[1]}`)
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
cpuPolygonPoints = `${
|
|
||||||
viewBoxX - (stats.length - 1)
|
|
||||||
},${viewBoxY} ${cpuPolylinePoints} ${viewBoxX},${viewBoxY}`;
|
|
||||||
memPolygonPoints = `${
|
|
||||||
viewBoxX - (stats.length - 1)
|
|
||||||
},${viewBoxY} ${memPolylinePoints} ${viewBoxX},${viewBoxY}`;
|
|
||||||
|
|
||||||
cpuHeadX = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][0];
|
|
||||||
cpuHeadY = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][1];
|
|
||||||
memHeadX = memPolylinePointsStats[memPolylinePointsStats.length - 1][0];
|
|
||||||
memHeadY = memPolylinePointsStats[memPolylinePointsStats.length - 1][1];
|
|
||||||
|
|
||||||
cpuP = (connStats.cpu * 100).toFixed(0);
|
|
||||||
memP = ((connStats.mem.active / props.meta.mem.total) * 100).toFixed(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onStatsLog(statsLog) {
|
|
||||||
for (const revStats of [...statsLog].reverse()) {
|
|
||||||
onStats(revStats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.lcfyofjk {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
width: 50%;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> text {
|
|
||||||
font-size: 5px;
|
|
||||||
fill: currentColor;
|
|
||||||
|
|
||||||
> tspan {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,65 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="vrvdvrys">
|
|
||||||
<XPie class="pie" :value="usage" />
|
|
||||||
<div>
|
|
||||||
<p><i class="ph-cpu ph-bold ph-lg"></i>CPU</p>
|
|
||||||
<p>{{ meta.cpu.cores }} Logical cores</p>
|
|
||||||
<p>{{ meta.cpu.model }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
|
||||||
import XPie from "./pie.vue";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
connection: any;
|
|
||||||
meta: any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let usage: number = $ref(0);
|
|
||||||
|
|
||||||
function onStats(stats) {
|
|
||||||
usage = stats.cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
props.connection.on("stats", onStats);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
props.connection.off("stats", onStats);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.vrvdvrys {
|
|
||||||
display: flex;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
> .pie {
|
|
||||||
height: 82px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.8em;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,57 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="zbwaqsat">
|
|
||||||
<XPie class="pie" :value="usage" />
|
|
||||||
<div>
|
|
||||||
<p><i class="ph-hard-drives ph-bold ph-lg"></i>Disk</p>
|
|
||||||
<p>Total: {{ bytes(total, 1) }}</p>
|
|
||||||
<p>Free: {{ bytes(available, 1) }}</p>
|
|
||||||
<p>Used: {{ bytes(used, 1) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {} from "vue";
|
|
||||||
import XPie from "./pie.vue";
|
|
||||||
import bytes from "@/filters/bytes";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
meta: any; // TODO
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const usage = $computed(() => props.meta.fs.used / props.meta.fs.total);
|
|
||||||
const total = $computed(() => props.meta.fs.total);
|
|
||||||
const used = $computed(() => props.meta.fs.used);
|
|
||||||
const available = $computed(() => props.meta.fs.total - props.meta.fs.used);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.zbwaqsat {
|
|
||||||
display: flex;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
> .pie {
|
|
||||||
height: 82px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.8em;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,135 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkContainer
|
|
||||||
:show-header="widgetProps.showHeader"
|
|
||||||
:naked="widgetProps.transparent"
|
|
||||||
>
|
|
||||||
<template #header
|
|
||||||
><i class="ph-hard-drives ph-bold ph-lg"></i
|
|
||||||
>{{ i18n.ts._widgets.serverMetric }}</template
|
|
||||||
>
|
|
||||||
<template #func
|
|
||||||
><button class="_button" @click="toggleView()">
|
|
||||||
<i class="ph-sort-ascending ph-bold ph-lg"></i></button
|
|
||||||
></template>
|
|
||||||
|
|
||||||
<div v-if="meta" class="mkw-serverMetric">
|
|
||||||
<XCpuMemory
|
|
||||||
v-if="widgetProps.view === 0"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
<XNet
|
|
||||||
v-else-if="widgetProps.view === 1"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
<XCpu
|
|
||||||
v-else-if="widgetProps.view === 2"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
<XMemory
|
|
||||||
v-else-if="widgetProps.view === 3"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
<XDisk
|
|
||||||
v-else-if="widgetProps.view === 4"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
<XMeili
|
|
||||||
v-else-if="
|
|
||||||
instance.features.searchFilters && widgetProps.view === 5
|
|
||||||
"
|
|
||||||
:connection="connection"
|
|
||||||
:meta="meta"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</MkContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
|
||||||
import {
|
|
||||||
useWidgetPropsManager,
|
|
||||||
Widget,
|
|
||||||
WidgetComponentEmits,
|
|
||||||
WidgetComponentExpose,
|
|
||||||
WidgetComponentProps,
|
|
||||||
} from "../widget";
|
|
||||||
import XCpuMemory from "./cpu-mem.vue";
|
|
||||||
import XNet from "./net.vue";
|
|
||||||
import XCpu from "./cpu.vue";
|
|
||||||
import XMemory from "./mem.vue";
|
|
||||||
import XDisk from "./disk.vue";
|
|
||||||
import XMeili from "./meilisearch.vue";
|
|
||||||
import MkContainer from "@/components/MkContainer.vue";
|
|
||||||
import { GetFormResultType } from "@/scripts/form";
|
|
||||||
import * as os from "@/os";
|
|
||||||
import { stream } from "@/stream";
|
|
||||||
import { i18n } from "@/i18n";
|
|
||||||
import { instance } from "@/instance";
|
|
||||||
|
|
||||||
const name = "serverMetric";
|
|
||||||
|
|
||||||
const widgetPropsDef = {
|
|
||||||
showHeader: {
|
|
||||||
type: "boolean" as const,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
transparent: {
|
|
||||||
type: "boolean" as const,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
view: {
|
|
||||||
type: "number" as const,
|
|
||||||
default: 0,
|
|
||||||
hidden: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
|
|
||||||
|
|
||||||
// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない
|
|
||||||
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
|
|
||||||
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
|
|
||||||
const props = defineProps<{ widget?: Widget<WidgetProps> }>();
|
|
||||||
const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
|
|
||||||
|
|
||||||
const { widgetProps, configure, save } = useWidgetPropsManager(
|
|
||||||
name,
|
|
||||||
widgetPropsDef,
|
|
||||||
props,
|
|
||||||
emit
|
|
||||||
);
|
|
||||||
|
|
||||||
const meta = ref(null);
|
|
||||||
|
|
||||||
os.api("server-info", {}).then((res) => {
|
|
||||||
meta.value = res;
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggleView = () => {
|
|
||||||
if (
|
|
||||||
(widgetProps.view === 5 && instance.features.searchFilters) ||
|
|
||||||
(widgetProps.view === 4 && !instance.features.searchFilters)
|
|
||||||
) {
|
|
||||||
widgetProps.view = 0;
|
|
||||||
} else {
|
|
||||||
widgetProps.view++;
|
|
||||||
}
|
|
||||||
save();
|
|
||||||
};
|
|
||||||
|
|
||||||
const connection = stream.useChannel("serverStats");
|
|
||||||
onUnmounted(() => {
|
|
||||||
connection.dispose();
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
|
||||||
name,
|
|
||||||
configure,
|
|
||||||
id: props.widget ? props.widget.id : null,
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -1,85 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="verusivbr">
|
|
||||||
<XPie
|
|
||||||
v-tooltip="i18n.ts.meiliIndexCount"
|
|
||||||
class="pie"
|
|
||||||
:value="progress"
|
|
||||||
:reverse="true"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<p><i class="ph-file-search ph-bold ph-lg"></i>MeiliSearch</p>
|
|
||||||
<p>{{ i18n.ts._widgets.meiliStatus }}: {{ available }}</p>
|
|
||||||
<p>{{ i18n.ts._widgets.meiliSize }}: {{ bytes(totalSize, 1) }}</p>
|
|
||||||
<p>{{ i18n.ts._widgets.meiliIndexCount }}: {{ indexCount }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onBeforeUnmount, onMounted } from "vue";
|
|
||||||
import bytes from "@/filters/bytes";
|
|
||||||
import XPie from "./pie.vue";
|
|
||||||
import { i18n } from "@/i18n";
|
|
||||||
import * as os from "@/os";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
connection: any;
|
|
||||||
meta: any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let progress: number = $ref(0);
|
|
||||||
let serverStats = $ref(null);
|
|
||||||
let totalSize: number = $ref(0);
|
|
||||||
let indexCount: number = $ref(0);
|
|
||||||
let available: string = $ref("unavailable");
|
|
||||||
|
|
||||||
function onStats(stats) {
|
|
||||||
totalSize = stats.meilisearch.size;
|
|
||||||
indexCount = stats.meilisearch.indexed_count;
|
|
||||||
available = stats.meilisearch.health;
|
|
||||||
progress = indexCount / serverStats.notesCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
os.api("stats", {}).then((res) => {
|
|
||||||
serverStats = res;
|
|
||||||
});
|
|
||||||
props.connection.on("stats", onStats);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
props.connection.off("stats", onStats);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.verusivbr {
|
|
||||||
display: flex;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
> .pie {
|
|
||||||
height: 82px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.8em;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,73 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="zlxnikvl">
|
|
||||||
<XPie class="pie" :value="usage" />
|
|
||||||
<div>
|
|
||||||
<p><i class="ph-microchip ph-bold ph-lg"></i>RAM</p>
|
|
||||||
<p>Total: {{ bytes(total, 1) }}</p>
|
|
||||||
<p>Used: {{ bytes(used, 1) }}</p>
|
|
||||||
<p>Free: {{ bytes(free, 1) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
|
||||||
import XPie from "./pie.vue";
|
|
||||||
import bytes from "@/filters/bytes";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
connection: any;
|
|
||||||
meta: any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let usage: number = $ref(0);
|
|
||||||
let total: number = $ref(0);
|
|
||||||
let used: number = $ref(0);
|
|
||||||
let free: number = $ref(0);
|
|
||||||
|
|
||||||
function onStats(stats) {
|
|
||||||
usage = stats.mem.active / props.meta.mem.total;
|
|
||||||
total = props.meta.mem.total;
|
|
||||||
used = stats.mem.active;
|
|
||||||
free = total - used;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
props.connection.on("stats", onStats);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
props.connection.off("stats", onStats);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.zlxnikvl {
|
|
||||||
display: flex;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
> .pie {
|
|
||||||
height: 82px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
> p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.8em;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,153 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="oxxrhrto">
|
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
|
||||||
<polygon
|
|
||||||
:points="inPolygonPoints"
|
|
||||||
fill="#f6c177"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
:points="inPolylinePoints"
|
|
||||||
fill="none"
|
|
||||||
stroke="#f6c177"
|
|
||||||
stroke-width="1"
|
|
||||||
/>
|
|
||||||
<circle :cx="inHeadX" :cy="inHeadY" r="1.5" fill="#f6c177" />
|
|
||||||
<text x="1" y="5">
|
|
||||||
NET rx
|
|
||||||
<tspan>{{ bytes(inRecent) }}</tspan>
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
|
||||||
<polygon
|
|
||||||
:points="outPolygonPoints"
|
|
||||||
fill="#31748f"
|
|
||||||
fill-opacity="0.5"
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
:points="outPolylinePoints"
|
|
||||||
fill="none"
|
|
||||||
stroke="#31748f"
|
|
||||||
stroke-width="1"
|
|
||||||
/>
|
|
||||||
<circle :cx="outHeadX" :cy="outHeadY" r="1.5" fill="#31748f" />
|
|
||||||
<text x="1" y="5">
|
|
||||||
NET tx
|
|
||||||
<tspan>{{ bytes(outRecent) }}</tspan>
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
|
||||||
import bytes from "@/filters/bytes";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
connection: any;
|
|
||||||
meta: any;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let viewBoxX: number = $ref(50);
|
|
||||||
let viewBoxY: number = $ref(30);
|
|
||||||
let stats: any[] = $ref([]);
|
|
||||||
let inPolylinePoints: string = $ref("");
|
|
||||||
let outPolylinePoints: string = $ref("");
|
|
||||||
let inPolygonPoints: string = $ref("");
|
|
||||||
let outPolygonPoints: string = $ref("");
|
|
||||||
let inHeadX: any = $ref(null);
|
|
||||||
let inHeadY: any = $ref(null);
|
|
||||||
let outHeadX: any = $ref(null);
|
|
||||||
let outHeadY: any = $ref(null);
|
|
||||||
let inRecent: number = $ref(0);
|
|
||||||
let outRecent: number = $ref(0);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
props.connection.on("stats", onStats);
|
|
||||||
props.connection.on("statsLog", onStatsLog);
|
|
||||||
props.connection.send("requestLog", {
|
|
||||||
id: Math.random().toString().substr(2, 8),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
props.connection.off("stats", onStats);
|
|
||||||
props.connection.off("statsLog", onStatsLog);
|
|
||||||
});
|
|
||||||
|
|
||||||
function onStats(connStats) {
|
|
||||||
stats.push(connStats);
|
|
||||||
if (stats.length > 50) stats.shift();
|
|
||||||
|
|
||||||
const inPeak = Math.max(1024 * 64, Math.max(...stats.map((s) => s.net.rx)));
|
|
||||||
const outPeak = Math.max(
|
|
||||||
1024 * 64,
|
|
||||||
Math.max(...stats.map((s) => s.net.tx))
|
|
||||||
);
|
|
||||||
|
|
||||||
let inPolylinePointsStats = stats.map((s, i) => [
|
|
||||||
viewBoxX - (stats.length - 1 - i),
|
|
||||||
(1 - s.net.rx / inPeak) * viewBoxY,
|
|
||||||
]);
|
|
||||||
let outPolylinePointsStats = stats.map((s, i) => [
|
|
||||||
viewBoxX - (stats.length - 1 - i),
|
|
||||||
(1 - s.net.tx / outPeak) * viewBoxY,
|
|
||||||
]);
|
|
||||||
inPolylinePoints = inPolylinePointsStats
|
|
||||||
.map((xy) => `${xy[0]},${xy[1]}`)
|
|
||||||
.join(" ");
|
|
||||||
outPolylinePoints = outPolylinePointsStats
|
|
||||||
.map((xy) => `${xy[0]},${xy[1]}`)
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
inPolygonPoints = `${
|
|
||||||
viewBoxX - (stats.length - 1)
|
|
||||||
},${viewBoxY} ${inPolylinePoints} ${viewBoxX},${viewBoxY}`;
|
|
||||||
outPolygonPoints = `${
|
|
||||||
viewBoxX - (stats.length - 1)
|
|
||||||
},${viewBoxY} ${outPolylinePoints} ${viewBoxX},${viewBoxY}`;
|
|
||||||
|
|
||||||
inHeadX = inPolylinePointsStats[inPolylinePointsStats.length - 1][0];
|
|
||||||
inHeadY = inPolylinePointsStats[inPolylinePointsStats.length - 1][1];
|
|
||||||
outHeadX = outPolylinePointsStats[outPolylinePointsStats.length - 1][0];
|
|
||||||
outHeadY = outPolylinePointsStats[outPolylinePointsStats.length - 1][1];
|
|
||||||
|
|
||||||
inRecent = connStats.net.rx;
|
|
||||||
outRecent = connStats.net.tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onStatsLog(statsLog) {
|
|
||||||
for (const revStats of [...statsLog].reverse()) {
|
|
||||||
onStats(revStats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.oxxrhrto {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
width: 50%;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> text {
|
|
||||||
font-size: 5px;
|
|
||||||
fill: currentColor;
|
|
||||||
|
|
||||||
> tspan {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,64 +0,0 @@
|
||||||
<template>
|
|
||||||
<svg class="hsalcinq" viewBox="0 0 1 1" preserveAspectRatio="none">
|
|
||||||
<circle
|
|
||||||
:r="r"
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
fill="none"
|
|
||||||
stroke-width="0.1"
|
|
||||||
stroke="rgba(0, 0, 0, 0.05)"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
:r="r"
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
:stroke-dasharray="Math.PI * (r * 2)"
|
|
||||||
:stroke-dashoffset="strokeDashoffset"
|
|
||||||
fill="none"
|
|
||||||
stroke-width="0.1"
|
|
||||||
:stroke="color"
|
|
||||||
/>
|
|
||||||
<text x="50%" y="50%" dy="0.05" text-anchor="middle">
|
|
||||||
{{ (value * 100).toFixed(0) }}%
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {} from "vue";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
value: number;
|
|
||||||
reverse?: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const r = 0.45;
|
|
||||||
|
|
||||||
const color = $computed(
|
|
||||||
() =>
|
|
||||||
`hsl(${
|
|
||||||
props.reverse ? props.value * 180 : 180 - props.value * 180
|
|
||||||
}, 80%, 70%)`
|
|
||||||
);
|
|
||||||
const strokeDashoffset = $computed(
|
|
||||||
() => (1 - props.value) * (Math.PI * (r * 2))
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.hsalcinq {
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
> circle {
|
|
||||||
transform-origin: center;
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
transition: stroke-dashoffset 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
> text {
|
|
||||||
font-size: 0.15px;
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
Reference in New Issue