@drocketxx/pm2me 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/db/index.js +6 -0
- package/backend/routes/api.js +14 -2
- package/package.json +1 -1
- package/apps/Delete-Test-App/README.md +0 -1
- package/apps/Delete-Test-App/dist/server.js +0 -57
- package/apps/Delete-Test-App/package.json +0 -19
- package/apps/Delete-Test-App/src/server.ts +0 -33
- package/apps/Delete-Test-App/tsconfig.json +0 -15
- package/apps/Delete-Test-App-2/README.md +0 -1
- package/apps/Delete-Test-App-2/dist/server.js +0 -57
- package/apps/Delete-Test-App-2/package.json +0 -19
- package/apps/Delete-Test-App-2/src/server.ts +0 -33
- package/apps/Delete-Test-App-2/tsconfig.json +0 -15
- package/apps/Delete-Test-App-2_deploy.log +0 -12
- package/apps/Delete-Test-App_deploy.log +0 -12
- package/apps/PM2Me-main/README.md +0 -236
- package/apps/PM2Me-main/backend/app.js +0 -109
- package/apps/PM2Me-main/backend/db/index.js +0 -40
- package/apps/PM2Me-main/backend/nodemon.json +0 -10
- package/apps/PM2Me-main/backend/package.json +0 -31
- package/apps/PM2Me-main/backend/public/assets/index-DZ4rSpP9.css +0 -1
- package/apps/PM2Me-main/backend/public/assets/index-KLmI9qSM.js +0 -13
- package/apps/PM2Me-main/backend/public/icon.png +0 -0
- package/apps/PM2Me-main/backend/public/index.html +0 -14
- package/apps/PM2Me-main/backend/public/vite.svg +0 -1
- package/apps/PM2Me-main/backend/routes/api.js +0 -932
- package/apps/PM2Me-main/backend/routes/auth.js +0 -44
- package/apps/PM2Me-main/backend/services/gitService.js +0 -110
- package/apps/PM2Me-main/backend/services/notificationService.js +0 -48
- package/apps/PM2Me-main/backend/services/pm2Service.js +0 -84
- package/apps/PM2Me-main/backend/services/systemService.js +0 -113
- package/apps/PM2Me-main/c:VsCodemyPM2Metmp_old_dashboard.vue +0 -390
- package/apps/PM2Me-main/package.json +0 -21
- package/apps/PM2Me-main/screenshot/001.png +0 -0
- package/apps/PM2Me-main/test_db.js +0 -8
- package/apps/PM2Me-main/tmp_old_dashboard.vue +0 -390
- package/apps/PM2Me-main_deploy.log +0 -25
- package/apps/PM2Me-test/README.md +0 -1
- package/apps/PM2Me-test/dist/server.js +0 -63
- package/apps/PM2Me-test/package.json +0 -19
- package/apps/PM2Me-test/src/server.ts +0 -33
- package/apps/PM2Me-test/tsconfig.json +0 -15
- package/apps/PM2Me-test-main/README.md +0 -1
- package/apps/PM2Me-test-main/dist/server.js +0 -63
- package/apps/PM2Me-test-main/package.json +0 -19
- package/apps/PM2Me-test-main/src/server.ts +0 -33
- package/apps/PM2Me-test-main/tsconfig.json +0 -15
- package/apps/PM2Me-test-main_deploy.log +0 -24
- package/apps/PM2Me-test-uat/README.md +0 -1
- package/apps/PM2Me-test-uat/dist/server.js +0 -63
- package/apps/PM2Me-test-uat/package.json +0 -19
- package/apps/PM2Me-test-uat/src/server.ts +0 -33
- package/apps/PM2Me-test-uat/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat-updated/README.md +0 -1
- package/apps/PM2Me-test-uat-updated/dist/server.js +0 -63
- package/apps/PM2Me-test-uat-updated/package.json +0 -19
- package/apps/PM2Me-test-uat-updated/src/server.ts +0 -33
- package/apps/PM2Me-test-uat-updated/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat-updated_deploy.log +0 -24
- package/apps/PM2Me-test-uat2/README.md +0 -1
- package/apps/PM2Me-test-uat2/dist/server.js +0 -63
- package/apps/PM2Me-test-uat2/package.json +0 -19
- package/apps/PM2Me-test-uat2/src/server.ts +0 -33
- package/apps/PM2Me-test-uat2/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat2_deploy.log +0 -25
- package/apps/PM2Me-test-uat_deploy.log +0 -24
- package/apps/PM2Me-test_deploy.log +0 -24
- package/apps/PM2Me-test_health.log +0 -4
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import db from '../db/index.js';
|
|
3
|
-
import bcrypt from 'bcrypt';
|
|
4
|
-
import jwt from 'jsonwebtoken';
|
|
5
|
-
|
|
6
|
-
const router = express.Router();
|
|
7
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'super-secret-pm2me-key-2026';
|
|
8
|
-
|
|
9
|
-
router.post('/login', async (req, res) => {
|
|
10
|
-
const { password } = req.body;
|
|
11
|
-
const adminConfig = db.data.admin;
|
|
12
|
-
|
|
13
|
-
if (!adminConfig || !adminConfig.passwordHash) {
|
|
14
|
-
// If no password is set, the first login attempts sets it
|
|
15
|
-
const hash = await bcrypt.hash(password, 10);
|
|
16
|
-
db.data.admin.passwordHash = hash;
|
|
17
|
-
await db.write();
|
|
18
|
-
const token = jwt.sign({ role: 'admin' }, JWT_SECRET, { expiresIn: '1d' });
|
|
19
|
-
return res.json({ token, message: 'Admin password set successfully and logged in' });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const match = await bcrypt.compare(password, adminConfig.passwordHash);
|
|
23
|
-
if (!match) {
|
|
24
|
-
return res.status(401).json({ error: 'Invalid password' });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const token = jwt.sign({ role: 'admin' }, JWT_SECRET, { expiresIn: '1d' });
|
|
28
|
-
res.json({ token });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export const authenticateToken = (req, res, next) => {
|
|
32
|
-
const authHeader = req.headers['authorization'];
|
|
33
|
-
const token = authHeader && authHeader.split(' ')[1];
|
|
34
|
-
|
|
35
|
-
if (!token) return res.sendStatus(401);
|
|
36
|
-
|
|
37
|
-
jwt.verify(token, JWT_SECRET, (err, user) => {
|
|
38
|
-
if (err) return res.sendStatus(403);
|
|
39
|
-
req.user = user;
|
|
40
|
-
next();
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default router;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
|
|
5
|
-
const getFinalUrl = (repoUrl, token) => {
|
|
6
|
-
let finalUrl = repoUrl;
|
|
7
|
-
if (token) {
|
|
8
|
-
try {
|
|
9
|
-
const urlObj = new URL(repoUrl);
|
|
10
|
-
urlObj.password = token;
|
|
11
|
-
urlObj.username = 'oauth2';
|
|
12
|
-
finalUrl = urlObj.toString();
|
|
13
|
-
} catch (e) {
|
|
14
|
-
console.error('Invalid URL for cloning', e);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return finalUrl;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const syncRepo = async (repoUrl, targetPath, branchName, token = null) => {
|
|
21
|
-
const finalUrl = getFinalUrl(repoUrl, token);
|
|
22
|
-
const envOptions = { ...process.env, GIT_TERMINAL_PROMPT: '0' }; // Prevent hanging on auth prompts or merge editors
|
|
23
|
-
|
|
24
|
-
const isGitRepo = fs.existsSync(path.join(targetPath, '.git'));
|
|
25
|
-
|
|
26
|
-
const freshClone = async () => {
|
|
27
|
-
if (fs.existsSync(targetPath)) {
|
|
28
|
-
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
29
|
-
}
|
|
30
|
-
fs.mkdirSync(targetPath, { recursive: true });
|
|
31
|
-
const git = simpleGit().env(envOptions);
|
|
32
|
-
await git.clone(finalUrl, targetPath, ['--branch', branchName]);
|
|
33
|
-
const gitLocal = simpleGit(targetPath);
|
|
34
|
-
const log = await gitLocal.log(['-1']);
|
|
35
|
-
const commitHash = log.latest.hash;
|
|
36
|
-
const commitMessage = log.latest.message;
|
|
37
|
-
return { message: 'Cloned repository cleanly', commitHash: commitHash.trim(), commitMessage: commitMessage.trim() };
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
if (!isGitRepo) {
|
|
41
|
-
try {
|
|
42
|
-
return await freshClone();
|
|
43
|
-
} catch (err) {
|
|
44
|
-
throw new Error(`Git Clone Error: ${err.message}`);
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
try {
|
|
48
|
-
const git = simpleGit(targetPath).env(envOptions);
|
|
49
|
-
await git.remote(['set-url', 'origin', finalUrl]);
|
|
50
|
-
await git.fetch('origin', branchName);
|
|
51
|
-
await git.checkout(branchName);
|
|
52
|
-
await git.reset(['--hard', 'FETCH_HEAD']);
|
|
53
|
-
await git.clean('f', ['-d']);
|
|
54
|
-
const log = await git.log(['-1']);
|
|
55
|
-
const commitHash = log.latest.hash;
|
|
56
|
-
const commitMessage = log.latest.message;
|
|
57
|
-
return { message: 'Pulled latest changes cleanly', commitHash: commitHash.trim(), commitMessage: commitMessage.trim() };
|
|
58
|
-
} catch (err) {
|
|
59
|
-
console.warn(`Git Sync failed, attempting fresh clone. Reason: ${err.message}`);
|
|
60
|
-
try {
|
|
61
|
-
return await freshClone();
|
|
62
|
-
} catch (cloneErr) {
|
|
63
|
-
throw new Error(`Git Sync & Retreat Clone Error: ${cloneErr.message}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export const getBranches = async (repoUrl, token = null) => {
|
|
70
|
-
const finalUrl = getFinalUrl(repoUrl, token);
|
|
71
|
-
const git = simpleGit().env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
72
|
-
const branches = await git.listRemote(['--heads', finalUrl]);
|
|
73
|
-
// Parse branch output, simplistic split
|
|
74
|
-
return branches.split('\n').filter(Boolean).map(line => line.split('refs/heads/')[1]);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Get the number of commits that the local branch is behind its remote origin counterpart.
|
|
79
|
-
*/
|
|
80
|
-
export const getBehindCount = async (repoUrl, targetPath, branchName, token = null) => {
|
|
81
|
-
const isGitRepo = fs.existsSync(path.join(targetPath, '.git'));
|
|
82
|
-
if (!isGitRepo) return 0;
|
|
83
|
-
|
|
84
|
-
const finalUrl = getFinalUrl(repoUrl, token);
|
|
85
|
-
const git = simpleGit(targetPath).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
await git.remote(['set-url', 'origin', finalUrl]);
|
|
89
|
-
await git.fetch(['origin', branchName]);
|
|
90
|
-
const count = await git.raw(['rev-list', '--count', `HEAD..origin/${branchName}`]);
|
|
91
|
-
return parseInt(count.trim(), 10) || 0;
|
|
92
|
-
} catch (err) {
|
|
93
|
-
console.error(`Git behind count error for ${targetPath}:`, err.message);
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export const checkout = async (targetPath, ref) => {
|
|
99
|
-
const git = simpleGit(targetPath).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
100
|
-
try {
|
|
101
|
-
await git.checkout(ref);
|
|
102
|
-
const log = await git.log(['-1']);
|
|
103
|
-
return {
|
|
104
|
-
hash: log.latest.hash.trim(),
|
|
105
|
-
message: log.latest.message.trim()
|
|
106
|
-
};
|
|
107
|
-
} catch (err) {
|
|
108
|
-
throw new Error(`Git Checkout Error: ${err.message}`);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import db from '../db/index.js';
|
|
2
|
-
|
|
3
|
-
export const sendDiscordNotification = async (message) => {
|
|
4
|
-
const { discordWebhook } = db.data.settings;
|
|
5
|
-
if (!discordWebhook) return;
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
await fetch(discordWebhook, {
|
|
9
|
-
method: 'POST',
|
|
10
|
-
headers: { 'Content-Type': 'application/json' },
|
|
11
|
-
body: JSON.stringify({ content: message })
|
|
12
|
-
});
|
|
13
|
-
} catch (err) {
|
|
14
|
-
console.error('Discord notification failed:', err);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const sendTelegramNotification = async (message) => {
|
|
19
|
-
const { telegramBotToken, telegramChatId } = db.data.settings;
|
|
20
|
-
if (!telegramBotToken || !telegramChatId) return;
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const url = `https://api.telegram.org/bot${telegramBotToken}/sendMessage`;
|
|
24
|
-
await fetch(url, {
|
|
25
|
-
method: 'POST',
|
|
26
|
-
headers: { 'Content-Type': 'application/json' },
|
|
27
|
-
body: JSON.stringify({ chat_id: telegramChatId, text: message })
|
|
28
|
-
});
|
|
29
|
-
} catch (err) {
|
|
30
|
-
console.error('Telegram notification failed:', err);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const notifyAll = async (appName, status, error = null) => {
|
|
35
|
-
let message = '';
|
|
36
|
-
if (status === 'success') {
|
|
37
|
-
message = `✅ **Deployment Success**\nApp \`${appName}\` has been successfully deployed and is now running.`;
|
|
38
|
-
} else if (status === 'failed') {
|
|
39
|
-
message = `❌ **Deployment Failed**\nApp \`${appName}\` failed to deploy.\nError: ${error}`;
|
|
40
|
-
} else {
|
|
41
|
-
message = `ℹ️ **App Status Update**\nApp \`${appName}\` is now ${status}.`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
await Promise.all([
|
|
45
|
-
sendDiscordNotification(message),
|
|
46
|
-
sendTelegramNotification(message)
|
|
47
|
-
]);
|
|
48
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import pm2 from 'pm2';
|
|
2
|
-
|
|
3
|
-
export const connectPM2 = () => {
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
pm2.connect((err) => {
|
|
6
|
-
if (err) return reject(err);
|
|
7
|
-
resolve(true);
|
|
8
|
-
});
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const listApps = () => {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
pm2.list((err, list) => {
|
|
15
|
-
if (err) return reject(err);
|
|
16
|
-
resolve(list);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const startApp = (options) => {
|
|
22
|
-
return new Promise((resolve, reject) => {
|
|
23
|
-
pm2.start(options, (err, apps) => {
|
|
24
|
-
if (err) return reject(err);
|
|
25
|
-
resolve(apps);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const stopApp = (nameOrId) => {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
pm2.stop(nameOrId, (err, proc) => {
|
|
33
|
-
if (err) return reject(err);
|
|
34
|
-
resolve(proc);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const restartApp = (nameOrId, options = {}) => {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
if (options && options.updateEnv) {
|
|
42
|
-
// To update env, restart should be called via start with appropriate options
|
|
43
|
-
pm2.start({ ...options, name: nameOrId, updateEnv: true }, (err, proc) => {
|
|
44
|
-
if (err) return reject(err);
|
|
45
|
-
resolve(proc);
|
|
46
|
-
});
|
|
47
|
-
} else {
|
|
48
|
-
pm2.restart(nameOrId, (err, proc) => {
|
|
49
|
-
if (err) return reject(err);
|
|
50
|
-
resolve(proc);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const reloadApp = (nameOrId, options = {}) => {
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
if (options && options.updateEnv) {
|
|
59
|
-
// pm2.reload doesn't accept options directly in all versions, start with updateEnv enforces a reload/restart
|
|
60
|
-
pm2.start({ ...options, name: nameOrId, updateEnv: true }, (err, proc) => {
|
|
61
|
-
if (err) return reject(err);
|
|
62
|
-
resolve(proc);
|
|
63
|
-
});
|
|
64
|
-
} else {
|
|
65
|
-
pm2.reload(nameOrId, (err, proc) => {
|
|
66
|
-
if (err) return reject(err);
|
|
67
|
-
resolve(proc);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const deleteApp = (nameOrId) => {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
pm2.delete(nameOrId, (err, proc) => {
|
|
76
|
-
if (err) return reject(err);
|
|
77
|
-
resolve(proc);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export const disconnectPM2 = () => {
|
|
83
|
-
pm2.disconnect();
|
|
84
|
-
};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import os from 'os';
|
|
2
|
-
import { exec } from 'child_process';
|
|
3
|
-
import util from 'util';
|
|
4
|
-
|
|
5
|
-
const execPromise = util.promisify(exec);
|
|
6
|
-
let lastNetworkStats = { rx: 0, tx: 0, time: Date.now() };
|
|
7
|
-
|
|
8
|
-
// Cache for slow stats
|
|
9
|
-
let cachedDiskStats = { total: 0, free: 0, used: 0, percentage: 0, lastFetch: 0 };
|
|
10
|
-
const DISK_CACHE_TTL = 60000; // 60 seconds
|
|
11
|
-
|
|
12
|
-
export const getSystemStats = async () => {
|
|
13
|
-
const uptime = os.uptime();
|
|
14
|
-
const totalMem = os.totalmem();
|
|
15
|
-
const freeMem = os.freemem();
|
|
16
|
-
const usedMem = totalMem - freeMem;
|
|
17
|
-
const memUsage = (usedMem / totalMem) * 100;
|
|
18
|
-
|
|
19
|
-
// CPU Usage (Load average or heuristic)
|
|
20
|
-
const cpus = os.cpus();
|
|
21
|
-
const cpuModel = cpus[0].model;
|
|
22
|
-
const loadAvg = os.loadavg();
|
|
23
|
-
let cpuUsage = os.platform() === 'win32' ? 0 : (loadAvg[0] / cpus.length) * 100;
|
|
24
|
-
let diskUsage = { total: 0, free: 0, used: 0, percentage: 0 };
|
|
25
|
-
|
|
26
|
-
// Network Usage
|
|
27
|
-
let networkUsage = { down: 0, up: 0 };
|
|
28
|
-
|
|
29
|
-
if (os.platform() === 'win32') {
|
|
30
|
-
try {
|
|
31
|
-
const now = Date.now();
|
|
32
|
-
const promises = [];
|
|
33
|
-
|
|
34
|
-
// 1. CPU Usage (Every time)
|
|
35
|
-
const cpuPromise = execPromise('powershell -Command "(Get-Counter \'\\Processor(_Total)\\% Processor Time\' -ErrorAction SilentlyContinue).CounterSamples.CookedValue"')
|
|
36
|
-
.then(({ stdout }) => {
|
|
37
|
-
const realCpu = parseFloat(stdout.trim());
|
|
38
|
-
if (!isNaN(realCpu)) cpuUsage = realCpu;
|
|
39
|
-
})
|
|
40
|
-
.catch(e => console.error('CPU fetch failed:', e));
|
|
41
|
-
promises.push(cpuPromise);
|
|
42
|
-
|
|
43
|
-
// 2. Network Stats (Every time)
|
|
44
|
-
const netPromise = execPromise('powershell -Command "Get-NetAdapterStatistics | Select-Object ReceivedBytes, SentBytes | ConvertTo-Json"')
|
|
45
|
-
.then(({ stdout }) => {
|
|
46
|
-
const netData = JSON.parse(stdout);
|
|
47
|
-
let totalRx = 0;
|
|
48
|
-
let totalTx = 0;
|
|
49
|
-
if (Array.isArray(netData)) {
|
|
50
|
-
netData.forEach(adapter => {
|
|
51
|
-
totalRx += adapter.ReceivedBytes || 0;
|
|
52
|
-
totalTx += adapter.SentBytes || 0;
|
|
53
|
-
});
|
|
54
|
-
} else if (netData) {
|
|
55
|
-
totalRx = netData.ReceivedBytes || 0;
|
|
56
|
-
totalTx = netData.SentBytes || 0;
|
|
57
|
-
}
|
|
58
|
-
const timeDiff = (now - lastNetworkStats.time) / 1000;
|
|
59
|
-
if (timeDiff > 0 && lastNetworkStats.rx > 0) {
|
|
60
|
-
networkUsage.down = Math.max(0, (totalRx - lastNetworkStats.rx) / timeDiff);
|
|
61
|
-
networkUsage.up = Math.max(0, (totalTx - lastNetworkStats.tx) / timeDiff);
|
|
62
|
-
}
|
|
63
|
-
lastNetworkStats = { rx: totalRx, tx: totalTx, time: now };
|
|
64
|
-
})
|
|
65
|
-
.catch(e => console.error('Network fetch failed:', e));
|
|
66
|
-
promises.push(netPromise);
|
|
67
|
-
|
|
68
|
-
// 3. Disk Stats (Cached)
|
|
69
|
-
if (now - cachedDiskStats.lastFetch > DISK_CACHE_TTL) {
|
|
70
|
-
const diskPromise = execPromise('powershell -Command "Get-Volume -DriveLetter C | Select-Object Size, SizeRemaining | ConvertTo-Json"')
|
|
71
|
-
.then(({ stdout }) => {
|
|
72
|
-
const data = JSON.parse(stdout);
|
|
73
|
-
if (data.Size > 0) {
|
|
74
|
-
cachedDiskStats = {
|
|
75
|
-
total: data.Size,
|
|
76
|
-
free: data.SizeRemaining,
|
|
77
|
-
used: data.Size - data.SizeRemaining,
|
|
78
|
-
percentage: ((data.Size - data.SizeRemaining) / data.Size) * 100,
|
|
79
|
-
lastFetch: now
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
.catch(e => console.error('Disk fetch failed:', e));
|
|
84
|
-
promises.push(diskPromise);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Run all in parallel
|
|
88
|
-
await Promise.all(promises);
|
|
89
|
-
diskUsage = cachedDiskStats; // Use either fresh or current cache
|
|
90
|
-
} catch (e) {
|
|
91
|
-
console.error('Failed to get system stats via PowerShell:', e);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
platform: os.platform(),
|
|
97
|
-
hostname: os.hostname(),
|
|
98
|
-
uptime,
|
|
99
|
-
cpu: {
|
|
100
|
-
model: cpuModel,
|
|
101
|
-
usage: cpuUsage,
|
|
102
|
-
cores: cpus.length
|
|
103
|
-
},
|
|
104
|
-
memory: {
|
|
105
|
-
total: totalMem,
|
|
106
|
-
free: freeMem,
|
|
107
|
-
used: usedMem,
|
|
108
|
-
percentage: memUsage
|
|
109
|
-
},
|
|
110
|
-
disk: diskUsage,
|
|
111
|
-
network: networkUsage
|
|
112
|
-
};
|
|
113
|
-
};
|