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
This commit is contained in:
parent
c2d5859755
commit
d8e1ab63c0
29 changed files with 279 additions and 396 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
25
packages/backend-rs/index.d.ts
vendored
25
packages/backend-rs/index.d.ts
vendored
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
2
packages/backend-rs/src/init/mod.rs
Normal file
2
packages/backend-rs/src/init/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod log;
|
||||
pub mod server_stats;
|
39
packages/backend-rs/src/init/server_stats.rs
Normal file
39
packages/backend-rs/src/init/server_stats.rs
Normal file
|
@ -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(())
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -15,3 +15,4 @@ pub mod nyaify;
|
|||
pub mod password;
|
||||
pub mod reaction;
|
||||
pub mod remove_old_attestation_challenges;
|
||||
pub mod server_stats;
|
||||
|
|
90
packages/backend-rs/src/misc/server_stats.rs
Normal file
90
packages/backend-rs/src/misc/server_stats.rs
Normal file
|
@ -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
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
pub mod log;
|
||||
pub mod nodeinfo;
|
||||
pub mod note;
|
||||
pub mod stream;
|
||||
|
|
|
@ -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",
|
||||
|
|
33
packages/backend/src/@types/os-utils.d.ts
vendored
33
packages/backend/src/@types/os-utils.d.ts
vendored
|
@ -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;
|
||||
}
|
|
@ -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)}`,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)`,
|
||||
);
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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: {}
|
||||
|
|
Loading…
Reference in a new issue