diff --git a/app/api/monitor/route.ts b/app/api/monitor/route.ts index 71e9e8e..9192bf6 100644 --- a/app/api/monitor/route.ts +++ b/app/api/monitor/route.ts @@ -2,6 +2,10 @@ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import authOptions, { getConnection } from "../auth/[...nextauth]/options"; +const fileds = [ + "used_memory", + "used_memory_rss" +] // eslint-disable-next-line import/prefer-default-export export async function GET() { @@ -16,20 +20,21 @@ export async function GET() { return NextResponse.json({ message: "Not authenticated" }, { status: 401 }) } - const info = await client.info("memory") - - if(typeof info === 'string') { - - const data = (info as string).split('\r\n').map((item) => { - const name = item.split(':')[0] - const num = item.split(':')[1] - return { name, series: num } - }) - - data.splice(0, 1) - return NextResponse.json(data, { status: 200 }) - } - - return NextResponse.json({message: "Failed to retrive info"}, { status: 500 }) + const infoMemory = await client.connection.info("memory") + const infoGraph = await client.info() + + const dataMemory = infoMemory.split('\r\n').map((item: string) => { + const name = item.split(':')[0] + const series = item.split(':')[1] + return { name, series } + }).filter((item: {name: string, series: string}) => fileds.find(filed => filed === item.name)) + const dataGraph: {name: string, series: number}[] = [] + for (let i = 0; i < infoGraph.length; i += 2) { + const name = (infoGraph[i] as string).substring(2) + const series = (infoGraph[i + 1] as string[]).length + dataGraph.push({name, series}) + } + + return NextResponse.json({ memory: dataMemory, graph: dataGraph }, { status: 200 }) } diff --git a/app/monitor/MonitorView.tsx b/app/monitor/MonitorView.tsx new file mode 100644 index 0000000..e2336f6 --- /dev/null +++ b/app/monitor/MonitorView.tsx @@ -0,0 +1,74 @@ +import { ECharts } from "echarts"; +import ReactEcharts, { EChartsInstance, EChartsOption } from "echarts-for-react" +import { useEffect, useRef, useState } from "react" + +interface Props { + data: { name: string, series: string }[] + time: Date +} + +export default function MonitorView({ data, time }: Props) { + + const echartRef = useRef(null) + const [timeArr] = useState([]) + const [chartReady, setChartReady] = useState(false) + + useEffect(() => { + if (chartReady && echartRef.current) { + const myChart: ECharts = echartRef.current + data.forEach((item, index) => { + myChart.appendData({ + seriesIndex: index, + data: [item.series] + }) + }) + timeArr.push(time.toLocaleTimeString().split(" ")[0]) + myChart.setOption({ + xAxis: { + type: "category", + data: timeArr + } + }) + } + }, [data, time, timeArr, chartReady]) + + const options: EChartsOption = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + }, + legend: { + data: data.map(item => item.name) + }, + xAxis: { + type: "category", + data: timeArr, + min: 0 + }, + yAxis: { + type: "value", + }, + series: data.map(item => ({ + name: item.name, + data: [], + type: "line", + smooth: true, + itemStyle: { + opacity: 0 + } + })) + } + + return ( + { + echartRef.current = e + setChartReady(true) + }} + /> + ) +} \ No newline at end of file diff --git a/app/monitor/page.tsx b/app/monitor/page.tsx new file mode 100644 index 0000000..ffe2f27 --- /dev/null +++ b/app/monitor/page.tsx @@ -0,0 +1,36 @@ +"use client" + +import useSWR from 'swr' +import React, { useState } from 'react' +import MonitorView from './MonitorView' + +export default function Page() { + + const [time, setTime] = useState(null) + + const fetcher = (url: string) => fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }).then((result) => { + if (result.status < 300) { + setTime(new Date()) + return result.json() + } + return [] + }) + + const { data } = useSWR(`/api/monitor/`, fetcher, { refreshInterval: 1000 }) + return ( +
+

Monitor

+
+ {(data?.memory && time) && } +
+
+ {(data?.graph && time) && } +
+
+ ) +} \ No newline at end of file diff --git a/app/providers.tsx b/app/providers.tsx index 4ecf6ee..c7bb452 100644 --- a/app/providers.tsx +++ b/app/providers.tsx @@ -2,7 +2,7 @@ import Navbar from "@/components/custom/navbar"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; -import { Info, LogOut, Waypoints } from "lucide-react"; +import { Activity, Info, LogOut, Waypoints } from "lucide-react"; import { SessionProvider, signOut } from "next-auth/react"; import { ThemeProvider } from 'next-themes' import { useEffect, useRef, useState } from "react"; @@ -20,6 +20,12 @@ const LINKS = [ href: "/graph", icon: (), }, + { + name: "Monitor", + // href: "/api/monitor", + href: "/monitor", + icon: (), + }, { name: "Disconnect", href: "", @@ -32,12 +38,12 @@ export default function NextAuthProvider({ children }: { children: React.ReactNo const { screenSize } = useScreenSize(); const isSmallScreen = screenSize === 'sm' || screenSize === 'xs' - + const [isCollapsed, setCollapsed] = useState(isSmallScreen) const navPanel = useRef(null) useEffect(() => { - if (isSmallScreen){ + if (isSmallScreen) { setCollapsed(true) if (navPanel.current) { navPanel.current.collapse() @@ -74,7 +80,7 @@ export default function NextAuthProvider({ children }: { children: React.ReactNo - {children} + {children} diff --git a/package-lock.json b/package-lock.json index f7178de..dad0464 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,8 @@ "cmdk": "^0.2.0", "cytoscape": "^3.28.1", "cytoscape-fcose": "^2.2.0", + "echarts": "^5.5.0", + "echarts-for-react": "^3.0.2", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", @@ -46,6 +48,7 @@ "react-json-tree": "^0.18.0", "react-resizable-panels": "^1.0.9", "save-dev": "^0.0.1-security", + "swr": "^2.2.5", "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", @@ -5563,6 +5566,33 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/echarts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.5.0" + } + }, + "node_modules/echarts-for-react": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz", + "integrity": "sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "size-sensor": "^1.0.1" + }, + "peerDependencies": { + "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0", + "react": "^15.0.0 || >=16.0.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "node_modules/electron-to-chromium": { "version": "1.4.708", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.708.tgz", @@ -8922,6 +8952,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/size-sensor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.2.tgz", + "integrity": "sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9230,6 +9265,18 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/swr": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwind-merge": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", @@ -9643,6 +9690,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9917,6 +9972,19 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zrender": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } } diff --git a/package.json b/package.json index 47f1656..e6968f0 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,8 @@ "cmdk": "^0.2.0", "cytoscape": "^3.28.1", "cytoscape-fcose": "^2.2.0", + "echarts": "^5.5.0", + "echarts-for-react": "^3.0.2", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", @@ -50,6 +52,7 @@ "react-json-tree": "^0.18.0", "react-resizable-panels": "^1.0.9", "save-dev": "^0.0.1-security", + "swr": "^2.2.5", "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7",