ezpm2gui 1.4.0 → 1.6.0
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/LICENSE +661 -0
- package/README.md +321 -295
- package/bin/ezpm2gui.js +10 -10
- package/bin/ezpm2gui.ts +51 -51
- package/bin/generate-ecosystem.js +36 -36
- package/bin/generate-ecosystem.ts +56 -56
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/server/config/project-configs.json +236 -0
- package/dist/server/index.js +214 -25
- package/dist/server/routes/deployApplication.js +6 -5
- package/dist/server/routes/pageAuth.d.ts +3 -0
- package/dist/server/routes/pageAuth.js +177 -0
- package/dist/server/routes/remoteConnections.js +260 -0
- package/dist/server/routes/updates.d.ts +3 -0
- package/dist/server/routes/updates.js +135 -0
- package/dist/server/utils/remote-connection.d.ts +18 -0
- package/dist/server/utils/remote-connection.js +216 -9
- package/package.json +73 -71
- package/scripts/postinstall.js +36 -36
- package/src/client/build/asset-manifest.json +6 -6
- package/src/client/build/favicon.ico +2 -2
- package/src/client/build/index.html +1 -1
- package/src/client/build/logo192.svg +7 -7
- package/src/client/build/logo512.svg +7 -7
- package/src/client/build/manifest.json +24 -24
- package/src/client/build/static/css/main.775772ee.css +5 -0
- package/src/client/build/static/css/main.775772ee.css.map +1 -0
- package/src/client/build/static/js/main.cbcb09c9.js +3 -0
- package/src/client/build/static/js/main.cbcb09c9.js.map +1 -0
- package/dist/server/config/cron-jobs.json +0 -1
- package/dist/server/config/remote-connections.json +0 -3
- package/dist/server/daemon/ezpm2gui.err.log +0 -414
- package/dist/server/daemon/ezpm2gui.exe +0 -0
- package/dist/server/daemon/ezpm2gui.exe.config +0 -6
- package/dist/server/daemon/ezpm2gui.out.log +0 -289
- package/dist/server/daemon/ezpm2gui.wrapper.log +0 -172
- package/dist/server/daemon/ezpm2gui.xml +0 -32
- package/src/client/build/static/css/main.c506cba5.css +0 -5
- package/src/client/build/static/css/main.c506cba5.css.map +0 -1
- package/src/client/build/static/js/main.5278cddd.js +0 -3
- package/src/client/build/static/js/main.5278cddd.js.map +0 -1
- /package/src/client/build/static/js/{main.5278cddd.js.LICENSE.txt → main.cbcb09c9.js.LICENSE.txt} +0 -0
|
@@ -171,6 +171,170 @@ class RemoteConnection extends events_1.EventEmitter {
|
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
|
+
static validateRemotePath(remotePath) {
|
|
175
|
+
if (!remotePath)
|
|
176
|
+
return false;
|
|
177
|
+
if (remotePath.includes('..'))
|
|
178
|
+
return false;
|
|
179
|
+
if (RemoteConnection.SHELL_UNSAFE.test(remotePath))
|
|
180
|
+
return false;
|
|
181
|
+
if (!/\.(log|gz)$/i.test(remotePath))
|
|
182
|
+
return false;
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
async streamFileToResponse(remotePath, res, fileName) {
|
|
186
|
+
// Validate path before any shell interpolation (fallback methods 2-4 use exec)
|
|
187
|
+
if (!RemoteConnection.validateRemotePath(remotePath)) {
|
|
188
|
+
if (!res.headersSent)
|
|
189
|
+
res.status(400).json({ success: false, error: 'Invalid log file path' });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const setHeaders = () => {
|
|
193
|
+
if (!res.headersSent) {
|
|
194
|
+
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
|
|
195
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const escapeRemotePathForShell = (value) => {
|
|
199
|
+
if (/[\0\n\r]/.test(value)) {
|
|
200
|
+
throw new Error('Invalid remote path');
|
|
201
|
+
}
|
|
202
|
+
return `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
203
|
+
};
|
|
204
|
+
let escapedRemotePath;
|
|
205
|
+
try {
|
|
206
|
+
escapedRemotePath = escapeRemotePathForShell(remotePath);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
if (!res.headersSent) {
|
|
210
|
+
res.status(400).json({ success: false, error: 'Invalid remote path' });
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// @group StreamFileToResponse : Helper — pipe an exec channel stdout to response
|
|
215
|
+
const pipeExec = (cmd, sudoPassword) => new Promise((resolve) => {
|
|
216
|
+
this.client.exec(cmd, (err, channel) => {
|
|
217
|
+
if (err) {
|
|
218
|
+
resolve('error');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (sudoPassword) {
|
|
222
|
+
channel.stdin.write(sudoPassword + '\n');
|
|
223
|
+
}
|
|
224
|
+
channel.stdin.end();
|
|
225
|
+
let hasData = false;
|
|
226
|
+
channel.on('data', (chunk) => {
|
|
227
|
+
if (!hasData) {
|
|
228
|
+
setHeaders();
|
|
229
|
+
hasData = true;
|
|
230
|
+
}
|
|
231
|
+
res.write(chunk);
|
|
232
|
+
});
|
|
233
|
+
channel.on('close', (code) => {
|
|
234
|
+
if (hasData) {
|
|
235
|
+
res.end();
|
|
236
|
+
resolve('ok');
|
|
237
|
+
}
|
|
238
|
+
else
|
|
239
|
+
resolve(code === 0 ? 'empty' : 'error');
|
|
240
|
+
});
|
|
241
|
+
channel.on('error', () => resolve('error'));
|
|
242
|
+
channel.stderr.resume(); // Discard stderr so it doesn't block the channel
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
// ── 1. SFTP (preferred — real streaming, no exec overhead) ──────────
|
|
246
|
+
const sftpOk = await new Promise((resolve) => {
|
|
247
|
+
this.client.sftp((err, sftp) => {
|
|
248
|
+
if (err) {
|
|
249
|
+
resolve(false);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const stream = sftp.createReadStream(remotePath);
|
|
253
|
+
let hasData = false;
|
|
254
|
+
stream.on('data', (chunk) => {
|
|
255
|
+
if (!hasData) {
|
|
256
|
+
setHeaders();
|
|
257
|
+
hasData = true;
|
|
258
|
+
}
|
|
259
|
+
res.write(chunk);
|
|
260
|
+
});
|
|
261
|
+
stream.on('end', () => { if (hasData) {
|
|
262
|
+
res.end();
|
|
263
|
+
resolve(true);
|
|
264
|
+
}
|
|
265
|
+
else
|
|
266
|
+
resolve(false); });
|
|
267
|
+
stream.on('error', () => resolve(false));
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
if (sftpOk)
|
|
271
|
+
return;
|
|
272
|
+
// ── 2. Plain cat ─────────────────────────────────────────────────────
|
|
273
|
+
if (await pipeExec(`cat -- ${escapedRemotePath}`) === 'ok')
|
|
274
|
+
return;
|
|
275
|
+
// ── 3. sudo -S cat (password auth) ───────────────────────────────────
|
|
276
|
+
if (this.config.password) {
|
|
277
|
+
if (await pipeExec(`sudo -S cat -- ${escapedRemotePath}`, this.config.password) === 'ok')
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// ── 4. sudo cat without -S (NOPASSWD / key-based auth) ───────────────
|
|
281
|
+
if (await pipeExec(`sudo cat -- ${escapedRemotePath}`) === 'ok')
|
|
282
|
+
return;
|
|
283
|
+
// All attempts failed
|
|
284
|
+
if (!res.headersSent) {
|
|
285
|
+
res.status(403).json({ success: false, error: 'Cannot read log file — check file permissions or sudo configuration' });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Stream a remote .gz file to an HTTP response, decompressing it on the fly.
|
|
290
|
+
* Uses SFTP + local zlib.createGunzip() pipeline — no server-side buffering.
|
|
291
|
+
* Falls back to exec `zcat` if SFTP is unavailable.
|
|
292
|
+
*/
|
|
293
|
+
async streamGzFileToResponse(remotePath, res, fileName) {
|
|
294
|
+
if (!RemoteConnection.validateRemotePath(remotePath)) {
|
|
295
|
+
if (!res.headersSent)
|
|
296
|
+
res.status(400).json({ success: false, error: 'Invalid log file path' });
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const zlib = require('zlib');
|
|
300
|
+
const setHeaders = () => {
|
|
301
|
+
if (!res.headersSent) {
|
|
302
|
+
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
|
|
303
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
// ── SFTP + local gunzip (preferred — no exec, streaming, no buffering) ──
|
|
307
|
+
const sftpOk = await new Promise((resolve) => {
|
|
308
|
+
this.client.sftp((err, sftp) => {
|
|
309
|
+
if (err) {
|
|
310
|
+
resolve(false);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const inputStream = sftp.createReadStream(remotePath);
|
|
314
|
+
const gunzip = zlib.createGunzip();
|
|
315
|
+
setHeaders();
|
|
316
|
+
inputStream.pipe(gunzip).pipe(res);
|
|
317
|
+
gunzip.on('finish', () => resolve(true));
|
|
318
|
+
gunzip.on('error', () => { inputStream.destroy(); resolve(false); });
|
|
319
|
+
inputStream.on('error', () => resolve(false));
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
if (sftpOk)
|
|
323
|
+
return;
|
|
324
|
+
// ── Fallback: exec zcat and buffer (path already validated) ──────────
|
|
325
|
+
if (!res.headersSent) {
|
|
326
|
+
const result = await this.executeCommand(`zcat -- "${remotePath}" 2>/dev/null`);
|
|
327
|
+
const decompressed = result.stdout ||
|
|
328
|
+
(await this.executeCommand(`sudo zcat -- "${remotePath}" 2>/dev/null`)).stdout;
|
|
329
|
+
if (decompressed) {
|
|
330
|
+
setHeaders();
|
|
331
|
+
res.send(decompressed);
|
|
332
|
+
}
|
|
333
|
+
else if (!res.headersSent) {
|
|
334
|
+
res.status(500).json({ success: false, error: 'Could not decompress file' });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
174
338
|
/**
|
|
175
339
|
* Check if PM2 is installed on the remote server
|
|
176
340
|
* Uses multiple detection methods for better reliability
|
|
@@ -317,15 +481,47 @@ class RemoteConnection extends events_1.EventEmitter {
|
|
|
317
481
|
}
|
|
318
482
|
try {
|
|
319
483
|
const processList = JSON.parse(cleanedOutput);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
484
|
+
// Return the full PM2 process shape (pm2_env + monit) so all frontend
|
|
485
|
+
// components work without modification when viewing a remote server.
|
|
486
|
+
return processList.map((proc) => {
|
|
487
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
488
|
+
return ({
|
|
489
|
+
pid: proc.pid || 0,
|
|
490
|
+
pm_id: proc.pm_id || 0,
|
|
491
|
+
name: proc.name || '',
|
|
492
|
+
monit: {
|
|
493
|
+
cpu: proc.monit ? (proc.monit.cpu || 0) : 0,
|
|
494
|
+
memory: proc.monit ? (proc.monit.memory || 0) : 0,
|
|
495
|
+
},
|
|
496
|
+
pm2_env: {
|
|
497
|
+
// identity
|
|
498
|
+
pm_id: proc.pm_id || 0,
|
|
499
|
+
name: proc.name || '',
|
|
500
|
+
namespace: ((_a = proc.pm2_env) === null || _a === void 0 ? void 0 : _a.namespace) || 'default',
|
|
501
|
+
version: ((_b = proc.pm2_env) === null || _b === void 0 ? void 0 : _b.version) || '',
|
|
502
|
+
versioning: ((_c = proc.pm2_env) === null || _c === void 0 ? void 0 : _c.versioning) || null,
|
|
503
|
+
// timing
|
|
504
|
+
pm_uptime: ((_d = proc.pm2_env) === null || _d === void 0 ? void 0 : _d.pm_uptime) || 0,
|
|
505
|
+
created_at: ((_e = proc.pm2_env) === null || _e === void 0 ? void 0 : _e.created_at) || 0,
|
|
506
|
+
// paths
|
|
507
|
+
pm_cwd: ((_f = proc.pm2_env) === null || _f === void 0 ? void 0 : _f.pm_cwd) || '',
|
|
508
|
+
pm_exec_path: ((_g = proc.pm2_env) === null || _g === void 0 ? void 0 : _g.pm_exec_path) || '',
|
|
509
|
+
pm_out_log_path: ((_h = proc.pm2_env) === null || _h === void 0 ? void 0 : _h.pm_out_log_path) || '',
|
|
510
|
+
pm_err_log_path: ((_j = proc.pm2_env) === null || _j === void 0 ? void 0 : _j.pm_err_log_path) || '',
|
|
511
|
+
// runtime
|
|
512
|
+
exec_interpreter: ((_k = proc.pm2_env) === null || _k === void 0 ? void 0 : _k.exec_interpreter) || 'node',
|
|
513
|
+
exec_mode: ((_l = proc.pm2_env) === null || _l === void 0 ? void 0 : _l.exec_mode) || 'fork',
|
|
514
|
+
instances: ((_m = proc.pm2_env) === null || _m === void 0 ? void 0 : _m.instances) || 1,
|
|
515
|
+
node_args: ((_o = proc.pm2_env) === null || _o === void 0 ? void 0 : _o.node_args) || [],
|
|
516
|
+
status: ((_p = proc.pm2_env) === null || _p === void 0 ? void 0 : _p.status) || 'unknown',
|
|
517
|
+
restart_time: ((_q = proc.pm2_env) === null || _q === void 0 ? void 0 : _q.restart_time) || 0,
|
|
518
|
+
unstable_restarts: ((_r = proc.pm2_env) === null || _r === void 0 ? void 0 : _r.unstable_restarts) || 0,
|
|
519
|
+
autorestart: (_t = (_s = proc.pm2_env) === null || _s === void 0 ? void 0 : _s.autorestart) !== null && _t !== void 0 ? _t : true,
|
|
520
|
+
watch: ((_u = proc.pm2_env) === null || _u === void 0 ? void 0 : _u.watch) || false,
|
|
521
|
+
env: ((_v = proc.pm2_env) === null || _v === void 0 ? void 0 : _v.env) || {},
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
});
|
|
329
525
|
}
|
|
330
526
|
catch (error) {
|
|
331
527
|
console.error('Error parsing PM2 process list:', error);
|
|
@@ -567,6 +763,17 @@ class RemoteConnection extends events_1.EventEmitter {
|
|
|
567
763
|
}
|
|
568
764
|
}
|
|
569
765
|
exports.RemoteConnection = RemoteConnection;
|
|
766
|
+
/**
|
|
767
|
+
* Stream a remote file directly into an HTTP response without buffering.
|
|
768
|
+
*
|
|
769
|
+
* Strategy (tried in order):
|
|
770
|
+
* 1. SFTP createReadStream — best; handles large files, uses file-transfer protocol
|
|
771
|
+
* 2. exec `cat <file>` — current SSH user, direct pipe to response
|
|
772
|
+
* 3. exec `sudo -S cat` — password-based sudo (when password auth is configured)
|
|
773
|
+
* 4. exec `sudo cat` — NOPASSWD sudo (key-based auth where sudo needs no password)
|
|
774
|
+
*/
|
|
775
|
+
// @group Security : Shell metacharacter allowlist for remote file paths
|
|
776
|
+
RemoteConnection.SHELL_UNSAFE = /['"`$|&;<>(){}\\\n\r\0]/;
|
|
570
777
|
/**
|
|
571
778
|
* Ordered list of command builders tried when resolving the pm2 binary.
|
|
572
779
|
* The index of the first successful builder is cached so that subsequent
|
package/package.json
CHANGED
|
@@ -1,71 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ezpm2gui",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"main": "dist/server/index.js",
|
|
5
|
-
"bin": {
|
|
6
|
-
"ezpm2gui": "bin/ezpm2gui.js",
|
|
7
|
-
"ezpm2gui-generate-ecosystem": "bin/generate-ecosystem.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"start": "node dist/server/index.js",
|
|
11
|
-
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
|
12
|
-
"dev:server": "nodemon --exec ts-node src/server/index.ts",
|
|
13
|
-
"dev:client": "cd src/client && npm
|
|
14
|
-
"build": "node scripts/build.js",
|
|
15
|
-
"build:server": "tsc",
|
|
16
|
-
"build:client": "cd src/client && npm run build",
|
|
17
|
-
"build:bin": "tsc --project tsconfig.bin.json",
|
|
18
|
-
"prepare": "npm run build",
|
|
19
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
20
|
-
"postinstall": "node scripts/postinstall.js"
|
|
21
|
-
},
|
|
22
|
-
"keywords": [
|
|
23
|
-
"pm2",
|
|
24
|
-
"gui",
|
|
25
|
-
"monitor",
|
|
26
|
-
"process-manager",
|
|
27
|
-
"dashboard"
|
|
28
|
-
],
|
|
29
|
-
"author": "Chandan Bhagat",
|
|
30
|
-
"license": "
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/thechandanbhagat/ezpm2gui.git"
|
|
34
|
-
},
|
|
35
|
-
"description": "A modern web-based GUI for PM2 process manager",
|
|
36
|
-
"files": [
|
|
37
|
-
"dist/",
|
|
38
|
-
"bin/",
|
|
39
|
-
"src/client/build/",
|
|
40
|
-
"scripts/postinstall.js"
|
|
41
|
-
],
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@tailwindcss/postcss": "^4.1.14",
|
|
44
|
-
"@types/node-cron": "^3.0.11",
|
|
45
|
-
"axios": "^1.9.0",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"react
|
|
53
|
-
"react-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"@types/
|
|
61
|
-
"@types/
|
|
62
|
-
"@types/react
|
|
63
|
-
"@types/
|
|
64
|
-
"@types/
|
|
65
|
-
"@types/
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "ezpm2gui",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"main": "dist/server/index.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"ezpm2gui": "bin/ezpm2gui.js",
|
|
7
|
+
"ezpm2gui-generate-ecosystem": "bin/generate-ecosystem.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node dist/server/index.js",
|
|
11
|
+
"dev": "concurrently --kill-others-on-fail --names \"server,client\" --prefix-colors \"cyan,green\" \"npm run dev:server\" \"npm run dev:client\"",
|
|
12
|
+
"dev:server": "nodemon --exec ts-node src/server/index.ts",
|
|
13
|
+
"dev:client": "cd src/client && npm run dev",
|
|
14
|
+
"build": "node scripts/build.js",
|
|
15
|
+
"build:server": "tsc",
|
|
16
|
+
"build:client": "cd src/client && npm run build",
|
|
17
|
+
"build:bin": "tsc --project tsconfig.bin.json",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
20
|
+
"postinstall": "node scripts/postinstall.js"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"pm2",
|
|
24
|
+
"gui",
|
|
25
|
+
"monitor",
|
|
26
|
+
"process-manager",
|
|
27
|
+
"dashboard"
|
|
28
|
+
],
|
|
29
|
+
"author": "Chandan Bhagat",
|
|
30
|
+
"license": "AGPL-3.0-or-later",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/thechandanbhagat/ezpm2gui.git"
|
|
34
|
+
},
|
|
35
|
+
"description": "A modern web-based GUI for PM2 process manager",
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/",
|
|
38
|
+
"bin/",
|
|
39
|
+
"src/client/build/",
|
|
40
|
+
"scripts/postinstall.js"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@tailwindcss/postcss": "^4.1.14",
|
|
44
|
+
"@types/node-cron": "^3.0.11",
|
|
45
|
+
"axios": "^1.9.0",
|
|
46
|
+
"dotenv": "^16.5.0",
|
|
47
|
+
"chart.js": "^4.4.9",
|
|
48
|
+
"cron-parser": "^5.4.0",
|
|
49
|
+
"express": "^4.18.3",
|
|
50
|
+
"node-cron": "^4.2.1",
|
|
51
|
+
"pm2": "^6.0.5",
|
|
52
|
+
"react": "^19.1.0",
|
|
53
|
+
"react-dom": "^19.1.0",
|
|
54
|
+
"react-scripts": "^5.0.1",
|
|
55
|
+
"socket.io": "^4.8.1",
|
|
56
|
+
"ssh2": "^1.16.0",
|
|
57
|
+
"uuid": "^11.0.5"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/express": "^4.17.21",
|
|
61
|
+
"@types/node": "^22.15.17",
|
|
62
|
+
"@types/react": "^19.1.4",
|
|
63
|
+
"@types/react-dom": "^19.1.5",
|
|
64
|
+
"@types/socket.io": "^3.0.2",
|
|
65
|
+
"@types/ssh2": "^1.15.5",
|
|
66
|
+
"@types/uuid": "^10.0.0",
|
|
67
|
+
"concurrently": "^9.1.2",
|
|
68
|
+
"cross-env": "^10.1.0",
|
|
69
|
+
"nodemon": "^3.1.10",
|
|
70
|
+
"ts-node": "^10.9.2",
|
|
71
|
+
"typescript": "^5.8.3"
|
|
72
|
+
}
|
|
73
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
console.log('Running postinstall script...');
|
|
6
|
-
|
|
7
|
-
const isGlobalInstall = process.env.npm_config_global === 'true';
|
|
8
|
-
if (isGlobalInstall) {
|
|
9
|
-
console.log('Global installation detected, skipping client dependencies.');
|
|
10
|
-
process.exit(0);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Check if we're in a development environment
|
|
14
|
-
const clientDir = path.join(__dirname, '..', 'src', 'client');
|
|
15
|
-
const clientNodeModules = path.join(clientDir, 'node_modules');
|
|
16
|
-
const packageJsonPath = path.join(clientDir, 'package.json');
|
|
17
|
-
|
|
18
|
-
if (fs.existsSync(packageJsonPath) && !fs.existsSync(clientNodeModules)) {
|
|
19
|
-
console.log('Installing client dependencies...');
|
|
20
|
-
try {
|
|
21
|
-
// Change to client directory
|
|
22
|
-
process.chdir(clientDir);
|
|
23
|
-
|
|
24
|
-
// Install dependencies
|
|
25
|
-
execSync('npm install', { stdio: 'inherit' });
|
|
26
|
-
|
|
27
|
-
console.log('Client dependencies installed successfully.');
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('Error installing client dependencies:', error.message);
|
|
30
|
-
console.error('Please run "cd src/client && npm install" manually.');
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
console.log('Client dependencies already installed or client package.json not found.');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('Postinstall completed.');
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
console.log('Running postinstall script...');
|
|
6
|
+
|
|
7
|
+
const isGlobalInstall = process.env.npm_config_global === 'true';
|
|
8
|
+
if (isGlobalInstall) {
|
|
9
|
+
console.log('Global installation detected, skipping client dependencies.');
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Check if we're in a development environment
|
|
14
|
+
const clientDir = path.join(__dirname, '..', 'src', 'client');
|
|
15
|
+
const clientNodeModules = path.join(clientDir, 'node_modules');
|
|
16
|
+
const packageJsonPath = path.join(clientDir, 'package.json');
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(packageJsonPath) && !fs.existsSync(clientNodeModules)) {
|
|
19
|
+
console.log('Installing client dependencies...');
|
|
20
|
+
try {
|
|
21
|
+
// Change to client directory
|
|
22
|
+
process.chdir(clientDir);
|
|
23
|
+
|
|
24
|
+
// Install dependencies
|
|
25
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
26
|
+
|
|
27
|
+
console.log('Client dependencies installed successfully.');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('Error installing client dependencies:', error.message);
|
|
30
|
+
console.error('Please run "cd src/client && npm install" manually.');
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
console.log('Client dependencies already installed or client package.json not found.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('Postinstall completed.');
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
|
-
"main.css": "/static/css/main.
|
|
4
|
-
"main.js": "/static/js/main.
|
|
3
|
+
"main.css": "/static/css/main.775772ee.css",
|
|
4
|
+
"main.js": "/static/js/main.cbcb09c9.js",
|
|
5
5
|
"index.html": "/index.html",
|
|
6
|
-
"main.
|
|
7
|
-
"main.
|
|
6
|
+
"main.775772ee.css.map": "/static/css/main.775772ee.css.map",
|
|
7
|
+
"main.cbcb09c9.js.map": "/static/js/main.cbcb09c9.js.map"
|
|
8
8
|
},
|
|
9
9
|
"entrypoints": [
|
|
10
|
-
"static/css/main.
|
|
11
|
-
"static/js/main.
|
|
10
|
+
"static/css/main.775772ee.css",
|
|
11
|
+
"static/js/main.cbcb09c9.js"
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
<!-- Simple favicon content - binary content approximated with HTML comment -->
|
|
2
|
-
<!-- This is a placeholder and should be replaced with a proper ICO file -->
|
|
1
|
+
<!-- Simple favicon content - binary content approximated with HTML comment -->
|
|
2
|
+
<!-- This is a placeholder and should be replaced with a proper ICO file -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#4a90e2"/><meta name="description" content="
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#4a90e2"/><meta name="description" content="EZ PM2 GUI - A modern interface for PM2 process manager"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>EZ PM2 GUI - PM2 Process Manager</title><script defer="defer" src="/static/js/main.cbcb09c9.js"></script><link href="/static/css/main.775772ee.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<rect width="192" height="192" rx="38.4" fill="#3F51B5"/>
|
|
3
|
-
<path d="M48 96C48 69.4904 69.4904 48 96 48C122.51 48 144 69.4904 144 96C144 122.51 122.51 144 96 144C69.4904 144 48 122.51 48 96Z" stroke="white" stroke-width="9.6"/>
|
|
4
|
-
<path d="M72 86.4H120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
5
|
-
<path d="M72 105.6H120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
6
|
-
<path d="M96 72V120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
7
|
-
</svg>
|
|
1
|
+
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="192" height="192" rx="38.4" fill="#3F51B5"/>
|
|
3
|
+
<path d="M48 96C48 69.4904 69.4904 48 96 48C122.51 48 144 69.4904 144 96C144 122.51 122.51 144 96 144C69.4904 144 48 122.51 48 96Z" stroke="white" stroke-width="9.6"/>
|
|
4
|
+
<path d="M72 86.4H120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
5
|
+
<path d="M72 105.6H120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
6
|
+
<path d="M96 72V120" stroke="white" stroke-width="9.6" stroke-linecap="round"/>
|
|
7
|
+
</svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<rect width="512" height="512" rx="102.4" fill="#3F51B5"/>
|
|
3
|
-
<path d="M128 256C128 185.307 185.307 128 256 128C326.693 128 384 185.307 384 256C384 326.693 326.693 384 256 384C185.307 384 128 326.693 128 256Z" stroke="white" stroke-width="25.6"/>
|
|
4
|
-
<path d="M192 230.4H320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
5
|
-
<path d="M192 281.6H320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
6
|
-
<path d="M256 192V320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
7
|
-
</svg>
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="512" height="512" rx="102.4" fill="#3F51B5"/>
|
|
3
|
+
<path d="M128 256C128 185.307 185.307 128 256 128C326.693 128 384 185.307 384 256C384 326.693 326.693 384 256 384C185.307 384 128 326.693 128 256Z" stroke="white" stroke-width="25.6"/>
|
|
4
|
+
<path d="M192 230.4H320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
5
|
+
<path d="M192 281.6H320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
6
|
+
<path d="M256 192V320" stroke="white" stroke-width="25.6" stroke-linecap="round"/>
|
|
7
|
+
</svg>
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"short_name": "
|
|
3
|
-
"name": "
|
|
4
|
-
"icons": [
|
|
5
|
-
{
|
|
6
|
-
"src": "favicon.ico",
|
|
7
|
-
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
-
"type": "image/x-icon"
|
|
9
|
-
}, {
|
|
10
|
-
"src": "logo192.svg",
|
|
11
|
-
"type": "image/svg+xml",
|
|
12
|
-
"sizes": "192x192"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"src": "logo512.svg",
|
|
16
|
-
"type": "image/svg+xml",
|
|
17
|
-
"sizes": "512x512"
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
"start_url": ".",
|
|
21
|
-
"display": "standalone",
|
|
22
|
-
"theme_color": "#4a90e2",
|
|
23
|
-
"background_color": "#f8f9fa"
|
|
24
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"short_name": "EZ PM2 GUI",
|
|
3
|
+
"name": "EZ PM2 GUI - Modern PM2 Interface",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
}, {
|
|
10
|
+
"src": "logo192.svg",
|
|
11
|
+
"type": "image/svg+xml",
|
|
12
|
+
"sizes": "192x192"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"src": "logo512.svg",
|
|
16
|
+
"type": "image/svg+xml",
|
|
17
|
+
"sizes": "512x512"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"start_url": ".",
|
|
21
|
+
"display": "standalone",
|
|
22
|
+
"theme_color": "#4a90e2",
|
|
23
|
+
"background_color": "#f8f9fa"
|
|
24
|
+
}
|