From d8e1ab63c0632ebe69f37755fc5fe4a364c9be52 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 15 May 2024 16:26:46 +0900
Subject: [PATCH] refactor: port system information checker to backend-rs

network stat is removed because it might be inaccurate and/or
it should be monitored by other system tools, but it may be added back
later if it is wanted
---
 Cargo.lock                                    |  35 ++++
 Cargo.toml                                    |   1 +
 docs/api-change.md                            |   2 +
 packages/backend-rs/Cargo.toml                |   1 +
 packages/backend-rs/index.d.ts                |  25 ++-
 packages/backend-rs/index.js                  |   9 +-
 .../backend-rs/src/{service => init}/log.rs   |   0
 packages/backend-rs/src/init/mod.rs           |   2 +
 packages/backend-rs/src/init/server_stats.rs  |  39 +++++
 packages/backend-rs/src/lib.rs                |   1 +
 packages/backend-rs/src/misc/mod.rs           |   1 +
 packages/backend-rs/src/misc/server_stats.rs  |  90 ++++++++++
 packages/backend-rs/src/service/mod.rs        |   1 -
 packages/backend/package.json                 |   2 -
 packages/backend/src/@types/os-utils.d.ts     |  33 ----
 packages/backend/src/boot/master.ts           |   6 +-
 packages/backend/src/daemons/server-stats.ts  |  60 +------
 .../backend/src/misc/show-machine-info.ts     |  17 --
 .../server/api/endpoints/admin/server-info.ts |  40 ++---
 .../src/server/api/endpoints/server-info.ts   |  33 ++--
 .../src/pages/admin/overview.metrics.vue      |   1 -
 .../src/widgets/server-metric/cpu-mem.vue     |  41 ++---
 .../client/src/widgets/server-metric/cpu.vue  |   4 +-
 .../client/src/widgets/server-metric/disk.vue |   9 +-
 .../src/widgets/server-metric/index.vue       |  34 ++--
 .../client/src/widgets/server-metric/mem.vue  |  11 +-
 .../client/src/widgets/server-metric/net.vue  | 156 ------------------
 .../client/src/widgets/server-metric/pie.vue  |   2 +-
 pnpm-lock.yaml                                |  19 ---
 29 files changed, 279 insertions(+), 396 deletions(-)
 rename packages/backend-rs/src/{service => init}/log.rs (100%)
 create mode 100644 packages/backend-rs/src/init/mod.rs
 create mode 100644 packages/backend-rs/src/init/server_stats.rs
 create mode 100644 packages/backend-rs/src/misc/server_stats.rs
 delete mode 100644 packages/backend/src/@types/os-utils.d.ts
 delete mode 100644 packages/backend/src/misc/show-machine-info.ts
 delete mode 100644 packages/client/src/widgets/server-metric/net.vue

diff --git a/Cargo.lock b/Cargo.lock
index 02eb95321e..95d2c9c03f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -236,6 +236,7 @@ dependencies = [
  "serde_json",
  "serde_yaml",
  "strum 0.26.2",
+ "sysinfo",
  "thiserror",
  "tokio",
  "tracing",
@@ -1644,6 +1645,15 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
 
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "nu-ansi-term"
 version = "0.46.0"
@@ -3167,6 +3177,21 @@ dependencies = [
  "syn 2.0.62",
 ]
 
+[[package]]
+name = "sysinfo"
+version = "0.30.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "rayon",
+ "windows",
+]
+
 [[package]]
 name = "system-deps"
 version = "6.2.2"
@@ -3673,6 +3698,16 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "windows-core"
 version = "0.52.0"
diff --git a/Cargo.toml b/Cargo.toml
index f8bb17f28b..827e4a2de3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ serde_json = "1.0.117"
 serde_yaml = "0.9.34"
 strum = "0.26.2"
 syn = "2.0.62"
+sysinfo = "0.30.12"
 thiserror = "1.0.60"
 tokio = "1.37.0"
 tracing = "0.1.40"
diff --git a/docs/api-change.md b/docs/api-change.md
index 5cbf3922d4..3c1cc3eb69 100644
--- a/docs/api-change.md
+++ b/docs/api-change.md
@@ -4,6 +4,8 @@ Breaking changes are indicated by the :warning: icon.
 
 ## Unreleased
 
+- :warning: `server-info` (an endpoint to get server hardware information) now requires credentials.
+- :warning: `net` (server's default network interface) has been removed from `admin/server-info`.
 - Adding `lang` to the response of `i` and the request parameter of `i/update`.
 
 ## v20240504
diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml
index 936fc525cf..52ae312872 100644
--- a/packages/backend-rs/Cargo.toml
+++ b/packages/backend-rs/Cargo.toml
@@ -39,6 +39,7 @@ serde = { workspace = true, features = ["derive"] }
 serde_json = { workspace = true }
 serde_yaml = { workspace = true }
 strum = { workspace = true, features = ["derive"] }
+sysinfo = { workspace = true }
 thiserror = { workspace = true }
 tokio = { workspace = true, features = ["full"] }
 tracing = { workspace = true }
diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts
index 1133fad209..6c549bd257 100644
--- a/packages/backend-rs/index.d.ts
+++ b/packages/backend-rs/index.d.ts
@@ -212,6 +212,8 @@ export interface Acct {
 }
 export function stringToAcct(acct: string): Acct
 export function acctToString(acct: Acct): string
+export function initializeRustLogger(): void
+export function showServerInfo(): void
 export function addNoteToAntenna(antennaId: string, note: Note): void
 /**
  * Checks if a server is blocked.
@@ -299,6 +301,28 @@ export function countReactions(reactions: Record<string, number>): Record<string
 export function toDbReaction(reaction?: string | undefined | null, host?: string | undefined | null): Promise<string>
 /** Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago */
 export function removeOldAttestationChallenges(): Promise<void>
+export interface Cpu {
+  model: string
+  cores: number
+}
+export interface Memory {
+  /** Total memory amount in bytes */
+  total: number
+  /** Used memory amount in bytes */
+  used: number
+  /** Available (for (re)use) memory amount in bytes */
+  available: number
+}
+export interface Storage {
+  /** Total storage space in bytes */
+  total: number
+  /** Used storage space in bytes */
+  used: number
+}
+export function cpuInfo(): Cpu
+export function cpuUsage(): number
+export function memoryUsage(): Memory
+export function storageUsage(): Storage | null
 export interface AbuseUserReport {
   id: string
   createdAt: Date
@@ -1156,7 +1180,6 @@ export interface Webhook {
   latestSentAt: Date | null
   latestStatus: number | null
 }
-export function initializeRustLogger(): void
 export function fetchNodeinfo(host: string): Promise<Nodeinfo>
 export function nodeinfo_2_1(): Promise<any>
 export function nodeinfo_2_0(): Promise<any>
diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js
index 287d4296fc..7819b4e7f1 100644
--- a/packages/backend-rs/index.js
+++ b/packages/backend-rs/index.js
@@ -310,7 +310,7 @@ if (!nativeBinding) {
   throw new Error(`Failed to load native binding`)
 }
 
-const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
+const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, initializeRustLogger, showServerInfo, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
 
 module.exports.SECOND = SECOND
 module.exports.MINUTE = MINUTE
@@ -323,6 +323,8 @@ module.exports.loadEnv = loadEnv
 module.exports.loadConfig = loadConfig
 module.exports.stringToAcct = stringToAcct
 module.exports.acctToString = acctToString
+module.exports.initializeRustLogger = initializeRustLogger
+module.exports.showServerInfo = showServerInfo
 module.exports.addNoteToAntenna = addNoteToAntenna
 module.exports.isBlockedServer = isBlockedServer
 module.exports.isSilencedServer = isSilencedServer
@@ -353,6 +355,10 @@ module.exports.decodeReaction = decodeReaction
 module.exports.countReactions = countReactions
 module.exports.toDbReaction = toDbReaction
 module.exports.removeOldAttestationChallenges = removeOldAttestationChallenges
+module.exports.cpuInfo = cpuInfo
+module.exports.cpuUsage = cpuUsage
+module.exports.memoryUsage = memoryUsage
+module.exports.storageUsage = storageUsage
 module.exports.AntennaSrcEnum = AntennaSrcEnum
 module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum
 module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
@@ -364,7 +370,6 @@ module.exports.RelayStatusEnum = RelayStatusEnum
 module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum
 module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum
 module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum
-module.exports.initializeRustLogger = initializeRustLogger
 module.exports.fetchNodeinfo = fetchNodeinfo
 module.exports.nodeinfo_2_1 = nodeinfo_2_1
 module.exports.nodeinfo_2_0 = nodeinfo_2_0
diff --git a/packages/backend-rs/src/service/log.rs b/packages/backend-rs/src/init/log.rs
similarity index 100%
rename from packages/backend-rs/src/service/log.rs
rename to packages/backend-rs/src/init/log.rs
diff --git a/packages/backend-rs/src/init/mod.rs b/packages/backend-rs/src/init/mod.rs
new file mode 100644
index 0000000000..0f6e5efebc
--- /dev/null
+++ b/packages/backend-rs/src/init/mod.rs
@@ -0,0 +1,2 @@
+pub mod log;
+pub mod server_stats;
diff --git a/packages/backend-rs/src/init/server_stats.rs b/packages/backend-rs/src/init/server_stats.rs
new file mode 100644
index 0000000000..9b99dba013
--- /dev/null
+++ b/packages/backend-rs/src/init/server_stats.rs
@@ -0,0 +1,39 @@
+use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
+use sysinfo::System;
+
+pub type SystemMutexError = PoisonError<MutexGuard<'static, System>>;
+
+// TODO: handle this in more proper way when we move the entry point to backend-rs
+pub fn system() -> Result<MutexGuard<'static, System>, SystemMutexError> {
+    pub static SYSTEM: OnceLock<Mutex<System>> = OnceLock::new();
+    SYSTEM.get_or_init(|| Mutex::new(System::new_all())).lock()
+}
+
+#[crate::export]
+pub fn show_server_info() -> Result<(), SystemMutexError> {
+    let system_info = system()?;
+
+    tracing::info!(
+        "Hostname: {}",
+        System::host_name().unwrap_or("unknown".to_string())
+    );
+    tracing::info!(
+        "OS: {}",
+        System::long_os_version().unwrap_or("unknown".to_string())
+    );
+    tracing::info!(
+        "Kernel: {}",
+        System::kernel_version().unwrap_or("unknown".to_string())
+    );
+    tracing::info!(
+        "CPU architecture: {}",
+        System::cpu_arch().unwrap_or("unknown".to_string())
+    );
+    tracing::info!("CPU threads: {}", system_info.cpus().len());
+    tracing::info!("Total memory: {} MiB", system_info.total_memory() / 1048576);
+    tracing::info!("Free memory: {} MiB", system_info.free_memory() / 1048576);
+    tracing::info!("Total swap: {} MiB", system_info.total_swap() / 1048576);
+    tracing::info!("Free swap: {} MiB", system_info.free_swap() / 1048576);
+
+    Ok(())
+}
diff --git a/packages/backend-rs/src/lib.rs b/packages/backend-rs/src/lib.rs
index 2a7f60111e..50a98ad787 100644
--- a/packages/backend-rs/src/lib.rs
+++ b/packages/backend-rs/src/lib.rs
@@ -3,6 +3,7 @@ pub use macro_rs::{export, ts_only_warn};
 pub mod config;
 pub mod database;
 pub mod federation;
+pub mod init;
 pub mod misc;
 pub mod model;
 pub mod service;
diff --git a/packages/backend-rs/src/misc/mod.rs b/packages/backend-rs/src/misc/mod.rs
index 8b81ccde1c..da65e26bc2 100644
--- a/packages/backend-rs/src/misc/mod.rs
+++ b/packages/backend-rs/src/misc/mod.rs
@@ -15,3 +15,4 @@ pub mod nyaify;
 pub mod password;
 pub mod reaction;
 pub mod remove_old_attestation_challenges;
+pub mod server_stats;
diff --git a/packages/backend-rs/src/misc/server_stats.rs b/packages/backend-rs/src/misc/server_stats.rs
new file mode 100644
index 0000000000..02a3ee08d4
--- /dev/null
+++ b/packages/backend-rs/src/misc/server_stats.rs
@@ -0,0 +1,90 @@
+use crate::init::server_stats::{system, SystemMutexError};
+use sysinfo::{Disks, MemoryRefreshKind};
+
+// TODO: i64 -> u64 (we can't export u64 to Node.js)
+
+#[crate::export(object)]
+pub struct Cpu {
+    pub model: String,
+    // TODO: u16 -> usize (we can't export usize to Node.js)
+    pub cores: u16,
+}
+
+#[crate::export(object)]
+pub struct Memory {
+    /// Total memory amount in bytes
+    pub total: i64,
+    /// Used memory amount in bytes
+    pub used: i64,
+    /// Available (for (re)use) memory amount in bytes
+    pub available: i64,
+}
+
+#[crate::export(object)]
+pub struct Storage {
+    /// Total storage space in bytes
+    pub total: i64,
+    /// Used storage space in bytes
+    pub used: i64,
+}
+
+#[crate::export]
+pub fn cpu_info() -> Result<Cpu, SystemMutexError> {
+    let system_info = system()?;
+
+    Ok(Cpu {
+        model: match system_info.cpus() {
+            cpus if cpus.is_empty() => {
+                tracing::debug!("failed to get CPU info");
+                "unknown".to_string()
+            }
+            cpus => cpus[0].brand().to_string(),
+        },
+        cores: system_info.cpus().len() as u16,
+    })
+}
+
+#[crate::export]
+pub fn cpu_usage() -> Result<f32, SystemMutexError> {
+    let mut system_info = system()?;
+    system_info.refresh_cpu_usage();
+
+    let total_cpu_usage: f32 = system_info.cpus().iter().map(|cpu| cpu.cpu_usage()).sum();
+    let cpu_threads = system_info.cpus().len();
+
+    Ok(total_cpu_usage / (cpu_threads as f32))
+}
+
+#[crate::export]
+pub fn memory_usage() -> Result<Memory, SystemMutexError> {
+    let mut system_info = system()?;
+
+    system_info.refresh_memory_specifics(MemoryRefreshKind::new().with_ram());
+
+    Ok(Memory {
+        total: system_info.total_memory() as i64,
+        used: system_info.used_memory() as i64,
+        available: system_info.available_memory() as i64,
+    })
+}
+
+#[crate::export]
+pub fn storage_usage() -> Option<Storage> {
+    // Get the first disk that is actualy used.
+    let disks = Disks::new_with_refreshed_list();
+    let disk = disks
+        .iter()
+        .find(|disk| disk.available_space() > 0 && disk.total_space() > disk.available_space());
+
+    if let Some(disk) = disk {
+        let total = disk.total_space() as i64;
+        let available = disk.available_space() as i64;
+        return Some(Storage {
+            total,
+            used: total - available,
+        });
+    }
+
+    tracing::debug!("failed to get stats");
+    None
+}
diff --git a/packages/backend-rs/src/service/mod.rs b/packages/backend-rs/src/service/mod.rs
index f755f22b4b..9569a33b63 100644
--- a/packages/backend-rs/src/service/mod.rs
+++ b/packages/backend-rs/src/service/mod.rs
@@ -1,4 +1,3 @@
-pub mod log;
 pub mod nodeinfo;
 pub mod note;
 pub mod stream;
diff --git a/packages/backend/package.json b/packages/backend/package.json
index cd9b157089..6a511e1b4a 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -87,7 +87,6 @@
 		"node-fetch": "3.3.2",
 		"nodemailer": "6.9.13",
 		"opencc-js": "1.0.5",
-		"os-utils": "0.0.14",
 		"otpauth": "9.2.4",
 		"parse5": "7.1.2",
 		"pg": "8.11.5",
@@ -111,7 +110,6 @@
 		"stringz": "2.1.0",
 		"summaly": "2.7.0",
 		"syslog-pro": "1.0.0",
-		"systeminformation": "5.22.8",
 		"tar-stream": "3.1.7",
 		"tesseract.js": "5.1.0",
 		"tinycolor2": "1.6.0",
diff --git a/packages/backend/src/@types/os-utils.d.ts b/packages/backend/src/@types/os-utils.d.ts
deleted file mode 100644
index 504096ae2b..0000000000
--- a/packages/backend/src/@types/os-utils.d.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-declare module "os-utils" {
-	type FreeCommandCallback = (usedmem: number) => void;
-
-	type HarddriveCallback = (total: number, free: number, used: number) => void;
-
-	type GetProcessesCallback = (result: string) => void;
-
-	type CPUCallback = (perc: number) => void;
-
-	export function platform(): NodeJS.Platform;
-	export function cpuCount(): number;
-	export function sysUptime(): number;
-	export function processUptime(): number;
-
-	export function freemem(): number;
-	export function totalmem(): number;
-	export function freememPercentage(): number;
-	export function freeCommand(callback: FreeCommandCallback): void;
-
-	export function harddrive(callback: HarddriveCallback): void;
-
-	export function getProcesses(callback: GetProcessesCallback): void;
-	export function getProcesses(
-		nProcess: number,
-		callback: GetProcessesCallback,
-	): void;
-
-	export function allLoadavg(): string;
-	export function loadavg(_time?: number): number;
-
-	export function cpuFree(callback: CPUCallback): void;
-	export function cpuUsage(callback: CPUCallback): void;
-}
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 1f63dc0136..68e7863628 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -12,10 +12,10 @@ import {
 	fetchMeta,
 	initializeRustLogger,
 	removeOldAttestationChallenges,
+	showServerInfo,
 	type Config,
 } from "backend-rs";
 import { config, envOption } from "@/config.js";
-import { showMachineInfo } from "@/misc/show-machine-info.js";
 import { db, initDb } from "@/db/postgre.js";
 import { inspect } from "node:util";
 
@@ -93,12 +93,12 @@ function greet() {
 export async function masterMain() {
 	// initialize app
 	try {
+		initializeRustLogger();
 		greet();
 		showEnvironment();
-		await showMachineInfo(bootLogger);
+		showServerInfo();
 		showNodejsVersion();
 		await connectDb();
-		initializeRustLogger();
 	} catch (e) {
 		bootLogger.error(
 			`Fatal error occurred during initialization:\n${inspect(e)}`,
diff --git a/packages/backend/src/daemons/server-stats.ts b/packages/backend/src/daemons/server-stats.ts
index df1f9b3032..2db09e8ae6 100644
--- a/packages/backend/src/daemons/server-stats.ts
+++ b/packages/backend/src/daemons/server-stats.ts
@@ -1,15 +1,8 @@
-import si from "systeminformation";
 import Xev from "xev";
-import * as osUtils from "os-utils";
-import { fetchMeta } from "backend-rs";
+import { fetchMeta, cpuUsage, memoryUsage } from "backend-rs";
 
 const ev = new Xev();
 
-const interval = 2000;
-
-const roundCpu = (num: number) => Math.round(num * 1000) / 1000;
-const round = (num: number) => Math.round(num * 10) / 10;
-
 /**
  * Report server stats regularly
  */
@@ -24,26 +17,9 @@ export default async function () {
 	if (!meta.enableServerMachineStats) return;
 
 	async function tick() {
-		const cpu = await cpuUsage();
-		const memStats = await mem();
-		const netStats = await net();
-		const fsStats = await fs();
-
 		const stats = {
-			cpu: roundCpu(cpu),
-			mem: {
-				used: round(memStats.used - memStats.buffers - memStats.cached),
-				active: round(memStats.active),
-				total: round(memStats.total),
-			},
-			net: {
-				rx: round(Math.max(0, netStats.rx_sec)),
-				tx: round(Math.max(0, netStats.tx_sec)),
-			},
-			fs: {
-				r: round(Math.max(0, fsStats.rIO_sec ?? 0)),
-				w: round(Math.max(0, fsStats.wIO_sec ?? 0)),
-			},
+			cpu: cpuUsage(),
+			mem: memoryUsage(),
 		};
 		ev.emit("serverStats", stats);
 		log.unshift(stats);
@@ -52,33 +28,5 @@ export default async function () {
 
 	tick();
 
-	setInterval(tick, interval);
-}
-
-// CPU STAT
-function cpuUsage(): Promise<number> {
-	return new Promise((res, rej) => {
-		osUtils.cpuUsage((cpuUsage) => {
-			res(cpuUsage);
-		});
-	});
-}
-
-// MEMORY STAT
-async function mem() {
-	const data = await si.mem();
-	return data;
-}
-
-// NETWORK STAT
-async function net() {
-	const iface = await si.networkInterfaceDefault();
-	const data = await si.networkStats(iface);
-	return data[0];
-}
-
-// FS STAT
-async function fs() {
-	const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
-	return data || { rIO_sec: 0, wIO_sec: 0 };
+	setInterval(tick, 3000);
 }
diff --git a/packages/backend/src/misc/show-machine-info.ts b/packages/backend/src/misc/show-machine-info.ts
deleted file mode 100644
index d3a28cbd37..0000000000
--- a/packages/backend/src/misc/show-machine-info.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as os from "node:os";
-import sysUtils from "systeminformation";
-import type Logger from "@/services/logger.js";
-
-export async function showMachineInfo(parentLogger: Logger) {
-	const logger = parentLogger.createSubLogger("machine");
-	logger.debug(`Hostname: ${os.hostname()}`);
-	logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`);
-	const mem = await sysUtils.mem();
-	const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
-	const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
-	logger.debug(
-		`CPU: ${
-			os.cpus().length
-		} core MEM: ${totalmem}GB (available: ${availmem}GB)`,
-	);
-}
diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts
index ff46919694..0faa7f7264 100644
--- a/packages/backend/src/server/api/endpoints/admin/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts
@@ -1,8 +1,12 @@
 import * as os from "node:os";
-import si from "systeminformation";
 import define from "@/server/api/define.js";
 import { redisClient } from "@/db/redis.js";
 import { db } from "@/db/postgre.js";
+import {
+	cpuInfo,
+	memoryUsage,
+	storageUsage,
+} from "backend-rs";
 
 export const meta = {
 	requireCredential: true,
@@ -85,19 +89,6 @@ export const meta = {
 					},
 				},
 			},
-			net: {
-				type: "object",
-				optional: false,
-				nullable: false,
-				properties: {
-					interface: {
-						type: "string",
-						optional: false,
-						nullable: false,
-						example: "eth0",
-					},
-				},
-			},
 		},
 	},
 } as const;
@@ -109,13 +100,10 @@ export const paramDef = {
 } as const;
 
 export default define(meta, paramDef, async () => {
-	const memStats = await si.mem();
-	const fsStats = await si.fsSize();
-	const netInterface = await si.networkInterfaceDefault();
-
 	const redisServerInfo = await redisClient.info("Server");
-	const m = redisServerInfo.match(new RegExp("^redis_version:(.*)", "m"));
+	const m = redisServerInfo.match(/^redis_version:(.*)/m);
 	const redis_version = m?.[1];
+	const storage = storageUsage();
 
 	return {
 		machine: os.hostname(),
@@ -125,19 +113,13 @@ export default define(meta, paramDef, async () => {
 			.query("SHOW server_version")
 			.then((x) => x[0].server_version),
 		redis: redis_version,
-		cpu: {
-			model: os.cpus()[0].model,
-			cores: os.cpus().length,
-		},
+		cpu: cpuInfo(),
 		mem: {
-			total: memStats.total,
+			total: memoryUsage().total,
 		},
 		fs: {
-			total: fsStats[0].size,
-			used: fsStats[0].used,
-		},
-		net: {
-			interface: netInterface,
+			total: storage?.total ?? 0,
+			used: storage?.used ?? 0,
 		},
 	};
 });
diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts
index 1a1ecad688..8f35daa7f8 100644
--- a/packages/backend/src/server/api/endpoints/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/server-info.ts
@@ -1,10 +1,9 @@
 import * as os from "node:os";
-import si from "systeminformation";
 import define from "@/server/api/define.js";
-import { fetchMeta } from "backend-rs";
+import { fetchMeta, cpuInfo, memoryUsage, storageUsage } from "backend-rs";
 
 export const meta = {
-	requireCredential: false,
+	requireCredential: true,
 	requireCredentialPrivateMode: true,
 	allowGet: true,
 	cacheSec: 30,
@@ -18,19 +17,8 @@ export const paramDef = {
 } as const;
 
 export default define(meta, paramDef, async () => {
-	const memStats = await si.mem();
-	const fsStats = await si.fsSize();
-
-	let fsIndex = 0;
-	// Get the first index of fs sizes that are actualy used.
-	for (const [i, stat] of fsStats.entries()) {
-		if (stat.rw === true && stat.used > 0) {
-			fsIndex = i;
-			break;
-		}
-	}
-
 	const instanceMeta = await fetchMeta(true);
+
 	if (!instanceMeta.enableServerMachineStats) {
 		return {
 			machine: "Not specified",
@@ -47,18 +35,19 @@ export default define(meta, paramDef, async () => {
 			},
 		};
 	}
+
+	const memory = memoryUsage();
+	const storage = storageUsage();
+
 	return {
 		machine: os.hostname(),
-		cpu: {
-			model: os.cpus()[0].model,
-			cores: os.cpus().length,
-		},
+		cpu: cpuInfo(),
 		mem: {
-			total: memStats.total,
+			total: memory.total,
 		},
 		fs: {
-			total: fsStats[fsIndex].size,
-			used: fsStats[fsIndex].used,
+			total: storage?.total ?? 0,
+			used: storage?.used ?? 0,
 		},
 	};
 });
diff --git a/packages/client/src/pages/admin/overview.metrics.vue b/packages/client/src/pages/admin/overview.metrics.vue
index 498659b194..165eadd852 100644
--- a/packages/client/src/pages/admin/overview.metrics.vue
+++ b/packages/client/src/pages/admin/overview.metrics.vue
@@ -44,7 +44,6 @@ import icon from "@/scripts/icon";
 const stream = useStream();
 
 const meta = await os.api("server-info", {});
-const serverStats = await os.api("stats");
 
 const cpuUsage = ref(0);
 
diff --git a/packages/client/src/widgets/server-metric/cpu-mem.vue b/packages/client/src/widgets/server-metric/cpu-mem.vue
index d304dc1627..bce50d94d5 100644
--- a/packages/client/src/widgets/server-metric/cpu-mem.vue
+++ b/packages/client/src/widgets/server-metric/cpu-mem.vue
@@ -36,7 +36,7 @@
 			/>
 			<text x="1" y="5">
 				CPU
-				<tspan>{{ cpuP }}%</tspan>
+				<tspan>{{ cpuUsage }}%</tspan>
 			</text>
 		</svg>
 		<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
@@ -75,7 +75,7 @@
 			/>
 			<text x="1" y="5">
 				MEM
-				<tspan>{{ memP }}%</tspan>
+				<tspan>{{ memUsage }}%</tspan>
 			</text>
 		</svg>
 	</div>
@@ -87,26 +87,25 @@ import { v4 as uuid } from "uuid";
 
 const props = defineProps<{
 	connection: any;
-	meta: any;
 }>();
 
-const viewBoxX: number = ref(50);
-const viewBoxY: number = ref(30);
-const stats: any[] = ref([]);
+const viewBoxX = ref(50);
+const viewBoxY = ref(30);
+const stats = ref<any[]>([]);
 const cpuGradientId = uuid();
 const cpuMaskId = uuid();
 const memGradientId = uuid();
 const memMaskId = uuid();
-const cpuPolylinePoints: string = ref("");
-const memPolylinePoints: string = ref("");
-const cpuPolygonPoints: string = ref("");
-const memPolygonPoints: string = ref("");
-const cpuHeadX: any = ref(null);
-const cpuHeadY: any = ref(null);
-const memHeadX: any = ref(null);
-const memHeadY: any = ref(null);
-const cpuP: string = ref("");
-const memP: string = ref("");
+const cpuPolylinePoints = ref("");
+const memPolylinePoints = ref("");
+const cpuPolygonPoints = ref("");
+const memPolygonPoints = ref("");
+const cpuHeadX = ref<number>();
+const cpuHeadY = ref<number>();
+const memHeadX = ref<number>();
+const memHeadY = ref<number>();
+const cpuUsage = ref<string>();
+const memUsage = ref<string>();
 
 onMounted(() => {
 	props.connection.on("stats", onStats);
@@ -127,11 +126,11 @@ function onStats(connStats) {
 
 	const cpuPolylinePointsStats = stats.value.map((s, i) => [
 		viewBoxX.value - (stats.value.length - 1 - i),
-		(1 - s.cpu) * viewBoxY.value,
+		(1 - s.cpu / 100) * viewBoxY.value,
 	]);
 	const memPolylinePointsStats = stats.value.map((s, i) => [
 		viewBoxX.value - (stats.value.length - 1 - i),
-		(1 - s.mem.active / s.mem.total) * viewBoxY.value,
+		(1 - s.mem.used / s.mem.total) * viewBoxY.value,
 	]);
 	cpuPolylinePoints.value = cpuPolylinePointsStats
 		.map((xy) => `${xy[0]},${xy[1]}`)
@@ -152,8 +151,10 @@ function onStats(connStats) {
 	memHeadX.value = memPolylinePointsStats[memPolylinePointsStats.length - 1][0];
 	memHeadY.value = memPolylinePointsStats[memPolylinePointsStats.length - 1][1];
 
-	cpuP.value = (connStats.cpu * 100).toFixed(0);
-	memP.value = ((connStats.mem.active / connStats.mem.total) * 100).toFixed(0);
+	cpuUsage.value = connStats.cpu.toFixed(1);
+	memUsage.value = ((connStats.mem.used / connStats.mem.total) * 100).toFixed(
+		1,
+	);
 }
 
 function onStatsLog(statsLog) {
diff --git a/packages/client/src/widgets/server-metric/cpu.vue b/packages/client/src/widgets/server-metric/cpu.vue
index 4a18098fb0..644db7dc91 100644
--- a/packages/client/src/widgets/server-metric/cpu.vue
+++ b/packages/client/src/widgets/server-metric/cpu.vue
@@ -19,10 +19,10 @@ const props = defineProps<{
 	meta: any;
 }>();
 
-const usage: number = ref(0);
+const usage = ref(0);
 
 function onStats(stats) {
-	usage.value = stats.cpu;
+	usage.value = stats.cpu / 100;
 }
 
 onMounted(() => {
diff --git a/packages/client/src/widgets/server-metric/disk.vue b/packages/client/src/widgets/server-metric/disk.vue
index 71ebd1732c..33d9c0eb54 100644
--- a/packages/client/src/widgets/server-metric/disk.vue
+++ b/packages/client/src/widgets/server-metric/disk.vue
@@ -4,7 +4,7 @@
 		<div>
 			<p><i :class="icon('ph-hard-drives')"></i>Disk</p>
 			<p>Total: {{ bytes(total, 1) }}</p>
-			<p>Free: {{ bytes(available, 1) }}</p>
+			<p>Available: {{ bytes(available, 1) }}</p>
 			<p>Used: {{ bytes(used, 1) }}</p>
 		</div>
 	</div>
@@ -18,7 +18,12 @@ import bytes from "@/filters/bytes";
 import icon from "@/scripts/icon";
 
 const props = defineProps<{
-	meta: any; // TODO
+	meta: {
+		fs: {
+			used: number;
+			total: number;
+		};
+	};
 }>();
 
 const usage = computed(() => props.meta.fs.used / props.meta.fs.total);
diff --git a/packages/client/src/widgets/server-metric/index.vue b/packages/client/src/widgets/server-metric/index.vue
index a9026efeac..6513b961a8 100644
--- a/packages/client/src/widgets/server-metric/index.vue
+++ b/packages/client/src/widgets/server-metric/index.vue
@@ -26,23 +26,18 @@
 				:connection="connection"
 				:meta="meta"
 			/>
-			<XNet
+			<XCpu
 				v-else-if="widgetProps.view === 1"
 				:connection="connection"
 				:meta="meta"
 			/>
-			<XCpu
+			<XMemory
 				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"
+				v-else-if="widgetProps.view === 3"
 				:connection="connection"
 				:meta="meta"
 			/>
@@ -52,10 +47,13 @@
 
 <script lang="ts" setup>
 import { onUnmounted, ref } from "vue";
-import type { Widget, WidgetComponentExpose } from "../widget";
+import type {
+	WidgetComponentEmits,
+	WidgetComponentExpose,
+	WidgetComponentProps,
+} from "../widget";
 import { useWidgetPropsManager } 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";
@@ -87,11 +85,8 @@ const widgetPropsDef = {
 
 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 props = defineProps<WidgetComponentProps<WidgetProps>>();
+const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
 
 const { widgetProps, configure, save } = useWidgetPropsManager(
 	name,
@@ -107,14 +102,7 @@ os.apiGet("server-info", {}).then((res) => {
 });
 
 const toggleView = () => {
-	if (
-		(widgetProps.view === 5 && instance.features.searchFilters) ||
-		(widgetProps.view === 4 && !instance.features.searchFilters)
-	) {
-		widgetProps.view = 0;
-	} else {
-		widgetProps.view++;
-	}
+	widgetProps.view = (widgetProps.view + 1) % 4;
 	save();
 };
 
diff --git a/packages/client/src/widgets/server-metric/mem.vue b/packages/client/src/widgets/server-metric/mem.vue
index b5ea5fd167..999b0e3d27 100644
--- a/packages/client/src/widgets/server-metric/mem.vue
+++ b/packages/client/src/widgets/server-metric/mem.vue
@@ -5,7 +5,7 @@
 			<p><i :class="icon('ph-microchip')"></i>RAM</p>
 			<p>Total: {{ bytes(total, 1) }}</p>
 			<p>Used: {{ bytes(used, 1) }}</p>
-			<p>Free: {{ bytes(free, 1) }}</p>
+			<p>Available: {{ bytes(available, 1) }}</p>
 		</div>
 	</div>
 </template>
@@ -18,19 +18,18 @@ import icon from "@/scripts/icon";
 
 const props = defineProps<{
 	connection: any;
-	meta: any;
 }>();
 
 const usage = ref<number>(0);
 const total = ref<number>(0);
 const used = ref<number>(0);
-const free = ref<number>(0);
+const available = ref<number>(0);
 
 function onStats(stats) {
-	usage.value = stats.mem.active / stats.mem.total;
+	usage.value = stats.mem.used / stats.mem.total;
 	total.value = stats.mem.total;
-	used.value = stats.mem.active;
-	free.value = total.value - used.value;
+	used.value = stats.mem.used;
+	available.value = stats.mem.available;
 }
 
 onMounted(() => {
diff --git a/packages/client/src/widgets/server-metric/net.vue b/packages/client/src/widgets/server-metric/net.vue
deleted file mode 100644
index 74571f2b10..0000000000
--- a/packages/client/src/widgets/server-metric/net.vue
+++ /dev/null
@@ -1,156 +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 { onBeforeUnmount, onMounted, ref } from "vue";
-import bytes from "@/filters/bytes";
-
-const props = defineProps<{
-	connection: any;
-	meta: any;
-}>();
-
-const viewBoxX: number = ref(50);
-const viewBoxY: number = ref(30);
-const stats: any[] = ref([]);
-const inPolylinePoints: string = ref("");
-const outPolylinePoints: string = ref("");
-const inPolygonPoints: string = ref("");
-const outPolygonPoints: string = ref("");
-const inHeadX: any = ref(null);
-const inHeadY: any = ref(null);
-const outHeadX: any = ref(null);
-const outHeadY: any = ref(null);
-const inRecent: number = ref(0);
-const outRecent: number = ref(0);
-
-onMounted(() => {
-	props.connection.on("stats", onStats);
-	props.connection.on("statsLog", onStatsLog);
-	props.connection.send("requestLog", {
-		id: Math.random().toString().substring(2, 10),
-	});
-});
-
-onBeforeUnmount(() => {
-	props.connection.off("stats", onStats);
-	props.connection.off("statsLog", onStatsLog);
-});
-
-function onStats(connStats) {
-	stats.value.push(connStats);
-	if (stats.value.length > 50) stats.value.shift();
-
-	const inPeak = Math.max(
-		1024 * 64,
-		Math.max(...stats.value.map((s) => s.net.rx)),
-	);
-	const outPeak = Math.max(
-		1024 * 64,
-		Math.max(...stats.value.map((s) => s.net.tx)),
-	);
-
-	const inPolylinePointsStats = stats.value.map((s, i) => [
-		viewBoxX.value - (stats.value.length - 1 - i),
-		(1 - s.net.rx / inPeak) * viewBoxY.value,
-	]);
-	const outPolylinePointsStats = stats.value.map((s, i) => [
-		viewBoxX.value - (stats.value.length - 1 - i),
-		(1 - s.net.tx / outPeak) * viewBoxY.value,
-	]);
-	inPolylinePoints.value = inPolylinePointsStats
-		.map((xy) => `${xy[0]},${xy[1]}`)
-		.join(" ");
-	outPolylinePoints.value = outPolylinePointsStats
-		.map((xy) => `${xy[0]},${xy[1]}`)
-		.join(" ");
-
-	inPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${
-		viewBoxY.value
-	} ${inPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
-	outPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${
-		viewBoxY.value
-	} ${outPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
-
-	inHeadX.value = inPolylinePointsStats[inPolylinePointsStats.length - 1][0];
-	inHeadY.value = inPolylinePointsStats[inPolylinePointsStats.length - 1][1];
-	outHeadX.value = outPolylinePointsStats[outPolylinePointsStats.length - 1][0];
-	outHeadY.value = outPolylinePointsStats[outPolylinePointsStats.length - 1][1];
-
-	inRecent.value = connStats.net.rx;
-	outRecent.value = 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>
diff --git a/packages/client/src/widgets/server-metric/pie.vue b/packages/client/src/widgets/server-metric/pie.vue
index 93880757b0..41addd4993 100644
--- a/packages/client/src/widgets/server-metric/pie.vue
+++ b/packages/client/src/widgets/server-metric/pie.vue
@@ -19,7 +19,7 @@
 			:stroke="color"
 		/>
 		<text x="50%" y="50%" dy="0.05" text-anchor="middle">
-			{{ (value * 100).toFixed(0) }}%
+			{{ (value * 100).toFixed(1) }}%
 		</text>
 	</svg>
 </template>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2936ba7e94..13cdd3d702 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -237,9 +237,6 @@ importers:
       opencc-js:
         specifier: 1.0.5
         version: 1.0.5
-      os-utils:
-        specifier: 0.0.14
-        version: 0.0.14
       otpauth:
         specifier: 9.2.4
         version: 9.2.4
@@ -309,9 +306,6 @@ importers:
       syslog-pro:
         specifier: 1.0.0
         version: 1.0.0
-      systeminformation:
-        specifier: 5.22.8
-        version: 5.22.8
       tar-stream:
         specifier: 3.1.7
         version: 3.1.7
@@ -6210,9 +6204,6 @@ packages:
     resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==}
     engines: {node: '>=4'}
 
-  os-utils@0.0.14:
-    resolution: {integrity: sha512-ajB8csaHLBvJOYsHJkp8YdO2FvlBbf/ZxaYQwXXRDyQ84UoE+uTuLXxqd0shekXMX6Qr/pt/DDyLMRAMsgfWzg==}
-
   otpauth@9.2.4:
     resolution: {integrity: sha512-t0Nioq2Up2ZaT5AbpXZLTjrsNtLc/g/rVSaEThmKLErAuT9mrnAKJryiPOKc3rCH+3ycWBgKpRHYn+DHqfaPiQ==}
 
@@ -7285,12 +7276,6 @@ packages:
     resolution: {integrity: sha512-7SNMJKtQBJlwBUp1jxFT7bXya71cnINXPCYJ2AVhlQE4MKL7o2QiPdAXbMdWRiLeykQ2rx+7TNrnoGzvzhO+eA==}
     engines: {node: '>=10.0.0'}
 
-  systeminformation@5.22.8:
-    resolution: {integrity: sha512-F1iWQ+PSfOzvLMGh2UXASaWLDq5o+1h1db13Kddl6ojcQ47rsJhpMtRrmBXfTA5QJgutC4KV67YRmXLuroIxrA==}
-    engines: {node: '>=8.0.0'}
-    os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
-    hasBin: true
-
   syuilo-password-strength@0.0.1:
     resolution: {integrity: sha512-g9rPT3V1Q4WjWFZ/t5BdGC1mT/FpYnsLdBl+M5e6MlRkuE1RSR+R43wcY/3mKI59B9KEr+vxdWCuWNMD3oNHKA==}
 
@@ -14397,8 +14382,6 @@ snapshots:
     dependencies:
       arch: 2.2.0
 
-  os-utils@0.0.14: {}
-
   otpauth@9.2.4:
     dependencies:
       jssha: 3.3.1
@@ -15537,8 +15520,6 @@ snapshots:
     dependencies:
       moment: 2.30.1
 
-  systeminformation@5.22.8: {}
-
   syuilo-password-strength@0.0.1: {}
 
   tabbable@6.2.0: {}