mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-19 05:05:44 +08:00
Introduced new interface definitions in napcat-common for logging, status, and subscription. Refactored napcat-webui-backend to use these interfaces, decoupling it from napcat-core and napcat-onebot. Moved OneBot config schema to backend and updated imports. Updated framework and shell to pass subscriptions to InitWebUi. Improved type safety and modularity across backend and shared packages.
139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
import os from 'node:os';
|
|
import EventEmitter from 'node:events';
|
|
import { IStatusHelperSubscription } from 'napcat-common/src/status-interface';
|
|
export interface SystemStatus {
|
|
cpu: {
|
|
model: string,
|
|
speed: string;
|
|
usage: {
|
|
system: string;
|
|
qq: string;
|
|
},
|
|
core: number;
|
|
},
|
|
memory: {
|
|
total: string;
|
|
usage: {
|
|
system: string;
|
|
qq: string;
|
|
};
|
|
},
|
|
arch: string;
|
|
}
|
|
|
|
export class StatusHelper {
|
|
private psCpuUsage = process.cpuUsage();
|
|
private psCurrentTime = process.hrtime();
|
|
private cpuTimes = os.cpus().map(cpu => cpu.times);
|
|
|
|
private replaceNaN (value: number) {
|
|
return isNaN(value) ? 0 : value;
|
|
}
|
|
|
|
private sysCpuInfo () {
|
|
const currentTimes = os.cpus().map(cpu => cpu.times);
|
|
const { total, active } = currentTimes.map((times, index) => {
|
|
const prevTimes = this.cpuTimes[index];
|
|
const totalCurrent = times.user + times.nice + times.sys + times.idle + times.irq;
|
|
const totalPrev = (prevTimes?.user ?? 0) + (prevTimes?.nice ?? 0) + (prevTimes?.sys ?? 0) + (prevTimes?.idle ?? 0) + (prevTimes?.irq ?? 0);
|
|
const activeCurrent = totalCurrent - times.idle;
|
|
const activePrev = totalPrev - (prevTimes?.idle ?? 0);
|
|
return {
|
|
total: totalCurrent - totalPrev,
|
|
active: activeCurrent - activePrev,
|
|
};
|
|
}).reduce((acc, cur) => ({
|
|
total: acc.total + cur.total,
|
|
active: acc.active + cur.active,
|
|
}), { total: 0, active: 0 });
|
|
this.cpuTimes = currentTimes;
|
|
return {
|
|
usage: this.replaceNaN(((active / total) * 100)).toFixed(2),
|
|
model: os.cpus()[0]?.model ?? 'none',
|
|
speed: os.cpus()[0]?.speed ?? 0,
|
|
core: os.cpus().length,
|
|
};
|
|
}
|
|
|
|
private sysMemoryUsage () {
|
|
const { total, free } = { total: os.totalmem(), free: os.freemem() };
|
|
return ((total - free) / 1024 / 1024).toFixed(2);
|
|
}
|
|
|
|
private qqUsage () {
|
|
const mem = process.memoryUsage();
|
|
const numCpus = os.cpus().length;
|
|
const usageDiff = process.cpuUsage(this.psCpuUsage);
|
|
const endTime = process.hrtime(this.psCurrentTime);
|
|
this.psCpuUsage = process.cpuUsage();
|
|
this.psCurrentTime = process.hrtime();
|
|
const usageMS = (usageDiff.user + usageDiff.system) / 1e3;
|
|
const totalMS = endTime[0] * 1e3 + endTime[1] / 1e6;
|
|
const normPercent = (usageMS / totalMS / numCpus) * 100;
|
|
return {
|
|
cpu: this.replaceNaN(normPercent).toFixed(2),
|
|
memory: ((mem.heapTotal + mem.external + mem.arrayBuffers) / 1024 / 1024).toFixed(2),
|
|
};
|
|
}
|
|
|
|
systemStatus (): SystemStatus {
|
|
const qqUsage = this.qqUsage();
|
|
const sysCpuInfo = this.sysCpuInfo();
|
|
return {
|
|
cpu: {
|
|
core: sysCpuInfo.core,
|
|
model: sysCpuInfo.model,
|
|
speed: (sysCpuInfo.speed / 1000).toFixed(2),
|
|
usage: {
|
|
system: sysCpuInfo.usage,
|
|
qq: qqUsage.cpu,
|
|
},
|
|
},
|
|
memory: {
|
|
total: (os.totalmem() / 1024 / 1024).toFixed(2),
|
|
usage: {
|
|
system: this.sysMemoryUsage(),
|
|
qq: qqUsage.memory,
|
|
},
|
|
},
|
|
arch: `${os.platform()} ${os.arch()} ${os.release()}`,
|
|
};
|
|
}
|
|
}
|
|
|
|
class StatusHelperSubscription extends EventEmitter implements IStatusHelperSubscription {
|
|
private statusHelper: StatusHelper;
|
|
private interval: NodeJS.Timeout | null = null;
|
|
|
|
constructor (time: number = 3000) {
|
|
super();
|
|
this.statusHelper = new StatusHelper();
|
|
this.on('newListener', (event: string) => {
|
|
if (event === 'statusUpdate' && this.listenerCount('statusUpdate') === 0) {
|
|
this.startInterval(time);
|
|
}
|
|
});
|
|
this.on('removeListener', (event: string) => {
|
|
if (event === 'statusUpdate' && this.listenerCount('statusUpdate') === 0) {
|
|
this.stopInterval();
|
|
}
|
|
});
|
|
}
|
|
|
|
private startInterval (time: number) {
|
|
this.interval ??= setInterval(() => {
|
|
const status = this.statusHelper.systemStatus();
|
|
this.emit('statusUpdate', status);
|
|
}, time);
|
|
}
|
|
|
|
private stopInterval () {
|
|
if (this.interval) {
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const statusHelperSubscription = new StatusHelperSubscription();
|