import si from "systeminformation";
import os from "os";
/**
* 过滤主要磁盘分区(跨平台)
*/
function filterMainDisks(disks: any[]) {
const platform = os.platform();
return disks.filter(d => {
const mount = d.mount.toLowerCase();
const fs = d.fs.toLowerCase();
// 排除太小的分区(小于 1GB,通常是系统分区)
if (d.size < 1024 * 1024 * 1024) return false;
if (platform === 'darwin') {
// macOS: 只保留用户数据分区和外部磁盘
return mount === '/system/volumes/data' ||
(mount.startsWith('/volumes/') &&
!mount.includes('preboot') &&
!mount.includes('recovery') &&
!mount.includes('vm'));
} else if (platform === 'win32') {
// Windows: 保留所有盘符(C:, D:, E: 等)
return mount.match(/^[a-z]:\\/i) !== null;
} else {
// Linux: 排除系统内部挂载点
const systemMounts = ['/boot', '/dev', '/proc', '/sys', '/run', '/snap'];
const isSystemMount = systemMounts.some(sm => mount.startsWith(sm));
// 保留根目录、home、用户挂载点
return !isSystemMount && (
mount === '/' ||
mount === '/home' ||
mount.startsWith('/mnt/') ||
mount.startsWith('/media/')
);
}
});
}
/**
* 过滤活跃的网络接口(跨平台)
*/
function filterActiveNetworks(interfaces: any[]) {
const platform = os.platform();
return interfaces
.filter(iface => {
// 排除内部回环接口
if (iface.internal) return false;
const name = iface.iface.toLowerCase();
// 平台特定的虚拟接口过滤
if (platform === 'darwin') {
// macOS: 排除 AirDrop、VPN、虚拟机等接口
if (name.startsWith('awdl') || name.startsWith('llw') ||
name.startsWith('utun') || name.startsWith('bridge') ||
name.startsWith('vmenet') || name.startsWith('anpi')) {
return false;
}
} else if (platform === 'win32') {
// Windows: 排除虚拟适配器
if (name.includes('loopback') || name.includes('tunnel') ||
name.includes('isatap') || name.includes('teredo')) {
return false;
}
} else {
// Linux: 排除虚拟接口
if (name.startsWith('veth') || name.startsWith('docker') ||
name.startsWith('br-') || name.startsWith('virbr') ||
name.startsWith('tun') || name.startsWith('tap')) {
return false;
}
}
// 必须有真实的 IPv4 地址,或者有非 link-local 的 IPv6 地址
const hasRealIp4 = iface.ip4 && iface.ip4.length > 0;
const hasRealIp6 = iface.ip6 && iface.ip6.length > 0 &&
!iface.ip6.startsWith('fe80::') &&
!iface.ip6.startsWith('::1');
return hasRealIp4 || hasRealIp6;
})
.map(iface => ({
iface: iface.iface,
ip4: iface.ip4,
ip6: iface.ip6 && !iface.ip6.startsWith('fe80::') ? iface.ip6 : undefined,
mac: iface.mac,
type: iface.type,
speed: iface.speed,
}));
}
export async function getHardwareInfo(detailed: boolean = false) {
try {
const cpu = await si.cpu();
const mem = await si.mem();
const disk = await si.fsSize();
const networkInterfaces = await si.networkInterfaces();
// 使用跨平台过滤函数
const mainDisks = filterMainDisks(disk);
const activeNetworks = filterActiveNetworks(networkInterfaces);
const basicInfo = {
cpu: {
manufacturer: cpu.manufacturer,
brand: cpu.brand,
speed: cpu.speed,
cores: cpu.cores,
physicalCores: cpu.physicalCores,
processors: cpu.processors,
},
memory: {
total: formatBytes(mem.total),
totalBytes: mem.total,
free: formatBytes(mem.free),
freeBytes: mem.free,
used: formatBytes(mem.used),
usedBytes: mem.used,
usagePercent: ((mem.used / mem.total) * 100).toFixed(2),
},
disk: mainDisks.map(d => ({
fs: d.fs,
type: d.type,
size: formatBytes(d.size),
sizeBytes: d.size,
used: formatBytes(d.used),
usedBytes: d.used,
available: formatBytes(d.available),
availableBytes: d.available,
usagePercent: d.use.toFixed(2),
mount: d.mount,
})),
network: activeNetworks,
};
if (detailed) {
const cpuTemp = await si.cpuTemperature();
const battery = await si.battery();
const graphics = await si.graphics();
return {
...basicInfo,
cpuTemperature: cpuTemp.main ? `${cpuTemp.main}°C` : "N/A",
battery: battery.hasBattery ? {
hasBattery: battery.hasBattery,
percent: battery.percent,
isCharging: battery.isCharging,
timeRemaining: battery.timeRemaining,
} : null,
graphics: graphics.controllers.map(g => ({
model: g.model,
vendor: g.vendor,
vram: g.vram,
vramDynamic: g.vramDynamic,
})),
};
}
return basicInfo;
} catch (error) {
if (error instanceof Error) {
throw new Error(`获取硬件信息失败: ${error.message}`);
}
throw error;
}
}
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
}