diff --git a/package.json b/package.json index dd8eef488..94be675e0 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "prism-react-renderer": "^1.3.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "systeminformation": "^5.11.16", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx index 36aad4bbd..9154fea10 100644 --- a/src/components/layout/Aside.tsx +++ b/src/components/layout/Aside.tsx @@ -1,5 +1,11 @@ import { Aside as MantineAside, Group } from '@mantine/core'; -import { WeatherModule, DateModule, CalendarModule, TotalDownloadsModule } from '../modules'; +import { + WeatherModule, + DateModule, + CalendarModule, + TotalDownloadsModule, + SystemModule, +} from '../modules'; import { ModuleWrapper } from '../modules/moduleWrapper'; export default function Aside(props: any) { @@ -15,11 +21,12 @@ export default function Aside(props: any) { base: 'auto', }} > - + + ); diff --git a/src/components/modules/calendar/CalendarModule.tsx b/src/components/modules/calendar/CalendarModule.tsx index 627d73e8b..058a81e7d 100644 --- a/src/components/modules/calendar/CalendarModule.tsx +++ b/src/components/modules/calendar/CalendarModule.tsx @@ -11,7 +11,7 @@ import { RadarrMediaDisplay, LidarrMediaDisplay, ReadarrMediaDisplay, -} from './MediaDisplay'; +} from '../common'; import { serviceItem } from '../../../tools/types'; export const CalendarModule: IModule = { diff --git a/src/components/modules/calendar/MediaDisplay.story.tsx b/src/components/modules/common/MediaDisplay.story.tsx similarity index 100% rename from src/components/modules/calendar/MediaDisplay.story.tsx rename to src/components/modules/common/MediaDisplay.story.tsx diff --git a/src/components/modules/calendar/MediaDisplay.tsx b/src/components/modules/common/MediaDisplay.tsx similarity index 98% rename from src/components/modules/calendar/MediaDisplay.tsx rename to src/components/modules/common/MediaDisplay.tsx index 5c27801bc..d8d945d67 100644 --- a/src/components/modules/calendar/MediaDisplay.tsx +++ b/src/components/modules/common/MediaDisplay.tsx @@ -14,7 +14,7 @@ export interface IMedia { episodeNumber?: number; } -function MediaDisplay(props: { media: IMedia }) { +export function MediaDisplay(props: { media: IMedia }) { const { media }: { media: IMedia } = props; return ( diff --git a/src/components/modules/common/index.ts b/src/components/modules/common/index.ts new file mode 100644 index 000000000..7b64171d2 --- /dev/null +++ b/src/components/modules/common/index.ts @@ -0,0 +1 @@ +export * from './MediaDisplay'; diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts index 410bf3b56..f7396ed66 100644 --- a/src/components/modules/index.ts +++ b/src/components/modules/index.ts @@ -4,3 +4,4 @@ export * from './search'; export * from './ping'; export * from './weather'; export * from './downloads'; +export * from './system'; diff --git a/src/components/modules/system/SystemModule.tsx b/src/components/modules/system/SystemModule.tsx new file mode 100644 index 000000000..2b84357f2 --- /dev/null +++ b/src/components/modules/system/SystemModule.tsx @@ -0,0 +1,64 @@ +import { + Center, + Group, + RingProgress, + Title, + useMantineTheme, +} from '@mantine/core'; +import { IconCpu } from '@tabler/icons'; +import { useEffect, useState } from 'react'; +import axios from 'axios'; +import si from 'systeminformation'; +import { useListState } from '@mantine/hooks'; +import { IModule } from '../modules'; + +export const SystemModule: IModule = { + title: 'System info', + description: 'Show the current CPU usage and memory usage', + icon: IconCpu, + component: SystemInfo, +}; + +interface ApiResponse { + cpu: si.Systeminformation.CpuData; + os: si.Systeminformation.OsData; + memory: si.Systeminformation.MemData; + load: si.Systeminformation.CurrentLoadData; +} + +export default function SystemInfo(args: any) { + const [data, setData] = useState(); + + // Refresh data every second + useEffect(() => { + setInterval(() => { + axios.get('/api/modules/systeminfo').then((res) => setData(res.data)); + }, 1000); + }, [args]); + + // Update data every time data changes + const [cpuLoadHistory, cpuLoadHistoryHandlers] = + useListState([]); + + // useEffect(() => { + + // }, [data]); + + const theme = useMantineTheme(); + const currentLoad = data?.load?.currentLoad ?? 0; + + return ( +
+ + Current CPU load + {`${currentLoad.toFixed(2)}%`}
} + thickness={15} + roundCaps + sections={[{ value: currentLoad ?? 0, color: 'cyan' }]} + /> +
+ + ); +} diff --git a/src/components/modules/system/index.ts b/src/components/modules/system/index.ts new file mode 100644 index 000000000..75d1a257e --- /dev/null +++ b/src/components/modules/system/index.ts @@ -0,0 +1 @@ +export { SystemModule } from './SystemModule'; diff --git a/src/pages/api/modules/systeminfo.ts b/src/pages/api/modules/systeminfo.ts new file mode 100644 index 000000000..2e4537e32 --- /dev/null +++ b/src/pages/api/modules/systeminfo.ts @@ -0,0 +1,30 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import si from 'systeminformation'; + +async function Get(req: NextApiRequest, res: NextApiResponse) { + const [osInfo, cpuInfo, memInfo, cpuLoad] = await Promise.all([ + si.osInfo(), + si.cpu(), + si.mem(), + si.currentLoad(), + ]); + + const sysinfo = { + cpu: cpuInfo, + os: osInfo, + mem: memInfo, + load: cpuLoad, + }; + res.status(200).json(sysinfo); +} + +export default async (req: NextApiRequest, res: NextApiResponse) => { + // Filter out if the reuqest is a POST or a GET + if (req.method === 'GET') { + return Get(req, res); + } + return res.status(405).json({ + statusCode: 405, + message: 'Method not allowed', + }); +}; diff --git a/yarn.lock b/yarn.lock index 0512feee1..c2d5107a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9444,6 +9444,7 @@ __metadata: react: ^17.0.1 react-dom: ^17.0.1 require-from-string: ^2.0.2 + systeminformation: ^5.11.16 typescript: 4.6.4 uuid: ^8.3.2 languageName: unknown @@ -15069,6 +15070,16 @@ __metadata: languageName: node linkType: hard +"systeminformation@npm:^5.11.16": + version: 5.11.16 + resolution: "systeminformation@npm:5.11.16" + bin: + systeminformation: lib/cli.js + checksum: 4e4fb4c9c86c658c7e07a7661ac85a102bfb0a134f76cc5c5e7daf7ba13f9b43895d4ce4d80e55275a3395d254d20a84c53f036f1baececf5b94028ec93242c4 + conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android) + languageName: node + linkType: hard + "tapable@npm:^1.0.0, tapable@npm:^1.1.3": version: 1.1.3 resolution: "tapable@npm:1.1.3"