mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
feat: system status helper
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/node": "^22.0.1",
|
"@types/node": "^22.0.1",
|
||||||
|
"@types/pidusage": "^2.0.5",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/ws": "^8.5.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
||||||
@@ -56,9 +57,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
|
"pidusage": "^3.0.2",
|
||||||
"piscina": "^4.7.0",
|
"piscina": "^4.7.0",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.6.1",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
104
src/core/helper/status.ts
Normal file
104
src/core/helper/status.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import os from "node:os";
|
||||||
|
import pidusage from 'pidusage';
|
||||||
|
import EventEmitter from "node:events";
|
||||||
|
|
||||||
|
export interface SystemStatus {
|
||||||
|
cpu: {
|
||||||
|
model: string,
|
||||||
|
speed: string
|
||||||
|
usage: {
|
||||||
|
system: string
|
||||||
|
qq: string
|
||||||
|
},
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
usage: {
|
||||||
|
system: string
|
||||||
|
qq: string
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatusHelper {
|
||||||
|
private get sysCpuInfo() {
|
||||||
|
const { total, active } = os.cpus().map(cpu => {
|
||||||
|
const times = cpu.times;
|
||||||
|
const total = times.user + times.nice + times.sys + times.idle + times.irq;
|
||||||
|
const active = total - times.idle;
|
||||||
|
return { total, active };
|
||||||
|
}).reduce((acc, cur) => ({
|
||||||
|
total: acc.total + cur.total,
|
||||||
|
active: acc.active + cur.active
|
||||||
|
}), { total: 0, active: 0 });
|
||||||
|
return {
|
||||||
|
usage: ((active / total) * 100).toFixed(2),
|
||||||
|
model: os.cpus()[0].model,
|
||||||
|
speed: os.cpus()[0].speed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private get sysMemoryUsage() {
|
||||||
|
const { total, free } = { total: os.totalmem(), free: os.freemem() };
|
||||||
|
return ((total - free) / total * 100).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async qqUsage() {
|
||||||
|
return await pidusage(process.pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
async systemStatus(): Promise<SystemStatus> {
|
||||||
|
const qqUsage = await this.qqUsage();
|
||||||
|
return {
|
||||||
|
cpu: {
|
||||||
|
model: this.sysCpuInfo.model,
|
||||||
|
speed: (this.sysCpuInfo.speed / 1000).toFixed(2),
|
||||||
|
usage: {
|
||||||
|
system: this.sysCpuInfo.usage,
|
||||||
|
qq: qqUsage.cpu.toFixed(2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
usage: {
|
||||||
|
system: this.sysMemoryUsage,
|
||||||
|
qq: (qqUsage.memory / os.totalmem() * 100).toFixed(2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusHelperSubscription extends EventEmitter {
|
||||||
|
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(async () => {
|
||||||
|
const status = await this.statusHelper.systemStatus();
|
||||||
|
this.emit('statusUpdate', status);
|
||||||
|
}, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopInterval() {
|
||||||
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const statusHelperSubscription = new StatusHelperSubscription();
|
19
src/webui/src/api/Status.ts
Normal file
19
src/webui/src/api/Status.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { RequestHandler } from 'express';
|
||||||
|
import { SystemStatus, statusHelperSubscription } from "@/core/helper/status";
|
||||||
|
|
||||||
|
export const StatusRealTimeHandler: RequestHandler = async (req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
|
res.setHeader('Connection', 'keep-alive');
|
||||||
|
const sendStatus = (status: SystemStatus) => {
|
||||||
|
try{
|
||||||
|
res.write(`data: ${JSON.stringify(status)}\n\n`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`An error occurred when writing sendStatus data to client: ${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
statusHelperSubscription.on('statusUpdate', sendStatus);
|
||||||
|
req.on('close', () => {
|
||||||
|
statusHelperSubscription.off('statusUpdate', sendStatus);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
};
|
@@ -1,8 +1,10 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import { PackageInfoHandler } from '../api/BaseInfo';
|
import { PackageInfoHandler } from '../api/BaseInfo';
|
||||||
|
import { StatusRealTimeHandler } from "@webapi/api/Status";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
// router: 获取nc的package.json信息
|
// router: 获取nc的package.json信息
|
||||||
router.get('/PackageInfo', PackageInfoHandler);
|
router.get('/PackageInfo', PackageInfoHandler);
|
||||||
|
router.get('/GetSysStatusRealTime', StatusRealTimeHandler);
|
||||||
|
|
||||||
export { router as BaseRouter };
|
export { router as BaseRouter };
|
||||||
|
1
src/webui/src/types/data.d.ts
vendored
1
src/webui/src/types/data.d.ts
vendored
@@ -1,4 +1,5 @@
|
|||||||
import type { LoginListItem, SelfInfo } from '@/core';
|
import type { LoginListItem, SelfInfo } from '@/core';
|
||||||
|
import type { OneBotConfig } from "@/onebot/config/config";
|
||||||
|
|
||||||
interface LoginRuntimeType {
|
interface LoginRuntimeType {
|
||||||
LoginCurrentTime: number;
|
LoginCurrentTime: number;
|
||||||
|
Reference in New Issue
Block a user