diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx
index 36aad4bbd..c856a4e31 100644
--- a/src/components/layout/Aside.tsx
+++ b/src/components/layout/Aside.tsx
@@ -1,5 +1,5 @@
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) {
@@ -20,6 +20,7 @@ export default function Aside(props: any) {
+
);
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..bcbc179f8
--- /dev/null
+++ b/src/components/modules/system/SystemModule.tsx
@@ -0,0 +1,46 @@
+import { Center, Group, RingProgress, Title } from '@mantine/core';
+import { Cpu } from 'tabler-icons-react';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
+import si from 'systeminformation';
+import { IModule } from '../modules';
+
+export const SystemModule: IModule = {
+ title: 'System info',
+ description: 'Show the current CPU usage and memory usage',
+ icon: Cpu,
+ 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 5 seconds
+ useEffect(() => {
+ axios.get('/api/modules/systeminfo').then((res) => setData(res.data));
+ setInterval(() => {
+ axios.get('/api/modules/systeminfo').then((res) => setData(res.data));
+ }, 3 * 1000);
+ }, []);
+
+ return (
+
+
+ Current CPU load
+ {`${data?.load?.currentLoad.toFixed(2)}%`}}
+ thickness={12}
+ roundCaps
+ sections={[{ value: data?.load?.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',
+ });
+};