ezpm2gui 1.0.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/ .npmignore ADDED
@@ -0,0 +1,39 @@
1
+ # Development files
2
+ src/
3
+ .git/
4
+ .github/
5
+ .gitignore
6
+ .editorconfig
7
+ .eslintrc
8
+ .prettierrc
9
+ tsconfig.json
10
+ nodemon.json
11
+
12
+ # Tests
13
+ __tests__/
14
+ coverage/
15
+ .nyc_output/
16
+
17
+ # Logs
18
+ logs/
19
+ *.log
20
+ npm-debug.log*
21
+ yarn-debug.log*
22
+ yarn-error.log*
23
+
24
+ # Cache and temp files
25
+ .cache/
26
+ .temp/
27
+ .tmp/
28
+ *.tmp
29
+ *.temp
30
+
31
+ # IDE specific files
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ *.swo
36
+ .DS_Store
37
+
38
+ # Include dist directory when publishing
39
+ !dist/
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # ezPM2GUI
2
+
3
+ A modern web-based graphical user interface for the PM2 process manager.
4
+
5
+ ![ezPM2GUI Screenshot](https://via.placeholder.com/800x450.png?text=ezPM2GUI+Screenshot)
6
+
7
+ ## Features
8
+
9
+ - Real-time process monitoring
10
+ - Process management (start, stop, restart, delete)
11
+ - Detailed process information and logs
12
+ - System metrics dashboard
13
+ - WebSocket for live updates
14
+ - Process CPU and memory charts
15
+ - Filter processes by status or name
16
+ - Responsive design for desktop and mobile
17
+
18
+ ## Installation
19
+
20
+ ### Global Installation
21
+
22
+ ```bash
23
+ npm install -g ezpm2gui
24
+ ```
25
+
26
+ ### Local Installation
27
+
28
+ ```bash
29
+ npm install ezpm2gui
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### As a Command Line Tool (Global Installation)
35
+
36
+ ```bash
37
+ # Start the ezPM2GUI web interface
38
+ ezpm2gui
39
+
40
+ # Generate a sample PM2 ecosystem config
41
+ ezpm2gui-generate-ecosystem
42
+ ```
43
+
44
+ ### As a Module (Local Installation)
45
+
46
+ ```javascript
47
+ const ezpm2gui = require('ezpm2gui');
48
+
49
+ // Start the server with default options
50
+ ezpm2gui.start();
51
+
52
+ // Or with custom options
53
+ ezpm2gui.start({
54
+ port: 3030,
55
+ host: '0.0.0.0'
56
+ });
57
+ ```
58
+
59
+ ### Access the UI
60
+
61
+ Once started, open your browser and navigate to:
62
+
63
+ ```
64
+ http://localhost:3001
65
+ ```
66
+
67
+ ## Requirements
68
+
69
+ - Node.js 14.x or later
70
+ - PM2 installed globally (`npm install -g pm2`)
71
+
72
+ ## Configuration
73
+
74
+ ezPM2GUI uses environment variables for configuration:
75
+
76
+ - `PORT`: The port to run the server on (default: 3001)
77
+ - `HOST`: The host to bind to (default: localhost)
78
+
79
+ ## Development
80
+
81
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for detailed development instructions.
82
+
83
+ ```bash
84
+ # Clone the repository
85
+ git clone https://github.com/yourusername/ezpm2gui.git
86
+ cd ezpm2gui
87
+
88
+ # Install dependencies and build
89
+ ./install.sh # On Linux/macOS
90
+ install.bat # On Windows
91
+
92
+ # Start the application
93
+ npm start
94
+ ```
95
+
96
+ ## License
97
+
98
+ ISC
99
+
100
+ ## Credits
101
+
102
+ Built with ❤️ as a modern alternative to pm2-gui
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // Determine the path to the server executable
8
+ const serverPath = path.join(__dirname, '../dist/server/index.js');
9
+
10
+ // Check if the server file exists
11
+ if (!fs.existsSync(serverPath)) {
12
+ console.error('Error: Server executable not found. Please make sure the package is built correctly.');
13
+ process.exit(1);
14
+ }
15
+
16
+ // Start the server
17
+ const server = spawn('node', [serverPath], { stdio: 'inherit' });
18
+
19
+ // Log startup message
20
+ console.log('\x1b[36m%s\x1b[0m', `
21
+ ╔════════════════════════════════════╗
22
+ ║ ezPM2GUI Started ║
23
+ ╚════════════════════════════════════╝
24
+
25
+ Web interface available at: \x1b[1mhttp://localhost:3001\x1b[0m
26
+
27
+ Press Ctrl+C to stop the server.
28
+ `);
29
+
30
+ // Handle server process events
31
+ server.on('error', (err) => {
32
+ console.error('Failed to start server:', err);
33
+ process.exit(1);
34
+ });
35
+
36
+ server.on('exit', (code) => {
37
+ if (code !== 0) {
38
+ console.error(`Server exited with code ${code}`);
39
+ }
40
+ process.exit(code);
41
+ });
42
+
43
+ // Handle process signals for graceful shutdown
44
+ process.on('SIGINT', () => {
45
+ console.log('Shutting down ezPM2GUI...');
46
+ server.kill('SIGINT');
47
+ });
48
+
49
+ process.on('SIGTERM', () => {
50
+ console.log('Shutting down ezPM2GUI...');
51
+ server.kill('SIGTERM');
52
+ });
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const ecosystemConfig = `
7
+ module.exports = {
8
+ apps : [
9
+ {
10
+ name: "my-api",
11
+ script: "./api/index.js",
12
+ instances: 2,
13
+ exec_mode: "cluster",
14
+ watch: false,
15
+ env: {
16
+ NODE_ENV: "production",
17
+ PORT: 3000
18
+ }
19
+ },
20
+ {
21
+ name: "worker",
22
+ script: "./workers/queue-processor.js",
23
+ instances: 1,
24
+ watch: false,
25
+ env: {
26
+ NODE_ENV: "production"
27
+ }
28
+ },
29
+ {
30
+ name: "cron-job",
31
+ script: "./cron/scheduler.js",
32
+ instances: 1,
33
+ watch: false,
34
+ cron_restart: "0 0 * * *",
35
+ env: {
36
+ NODE_ENV: "production"
37
+ }
38
+ }
39
+ ]
40
+ }
41
+ `;
42
+
43
+ // Get the output path
44
+ const outputPath = process.argv[2] || 'ecosystem.config.js';
45
+ const fullPath = path.resolve(process.cwd(), outputPath);
46
+
47
+ try {
48
+ // Write file
49
+ fs.writeFileSync(fullPath, ecosystemConfig);
50
+ console.log(`PM2 ecosystem file generated at: ${fullPath}`);
51
+ console.log('\nTo start your PM2 processes with this configuration:');
52
+ console.log(' pm2 start ecosystem.config.js');
53
+ console.log('\nTo monitor your processes with ezPM2GUI:');
54
+ console.log(' ezpm2gui');
55
+ } catch (error) {
56
+ console.error('Error generating ecosystem file:', error);
57
+ }
@@ -0,0 +1,14 @@
1
+ interface StartOptions {
2
+ port?: number;
3
+ host?: string;
4
+ }
5
+ /**
6
+ * Start the ezPM2GUI server
7
+ * @param options Configuration options
8
+ * @returns The HTTP server instance
9
+ */
10
+ export declare function start(options?: StartOptions): import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
11
+ declare const _default: {
12
+ start: typeof start;
13
+ };
14
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.start = start;
4
+ // src/index.ts - Main entry point for programmatic usage
5
+ const index_1 = require("./server/index");
6
+ /**
7
+ * Start the ezPM2GUI server
8
+ * @param options Configuration options
9
+ * @returns The HTTP server instance
10
+ */
11
+ function start(options = {}) {
12
+ const port = options.port || process.env.PORT || 3001;
13
+ const host = options.host || process.env.HOST || 'localhost';
14
+ process.env.PORT = port.toString();
15
+ process.env.HOST = host;
16
+ return (0, index_1.createServer)();
17
+ }
18
+ exports.default = { start };
@@ -0,0 +1,5 @@
1
+ import http from 'http';
2
+ /**
3
+ * Create and configure the express server
4
+ */
5
+ export declare function createServer(): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createServer = createServer;
7
+ const express_1 = __importDefault(require("express"));
8
+ const http_1 = __importDefault(require("http"));
9
+ const socket_io_1 = require("socket.io");
10
+ const pm2_1 = __importDefault(require("pm2"));
11
+ const os_1 = __importDefault(require("os"));
12
+ /**
13
+ * Create and configure the express server
14
+ */
15
+ function createServer() {
16
+ // Initialize Express app
17
+ const app = (0, express_1.default)();
18
+ const server = http_1.default.createServer(app);
19
+ const io = new socket_io_1.Server(server, {
20
+ cors: {
21
+ origin: '*',
22
+ methods: ['GET', 'POST']
23
+ }
24
+ }); // Serve static files from the React app build directory
25
+ const staticPath = 'D:/Personal/ezpm2gui/src/client/build';
26
+ console.log('Serving static files from:', staticPath);
27
+ const fs = require('fs');
28
+ if (fs.existsSync(staticPath)) {
29
+ app.use(express_1.default.static(staticPath));
30
+ }
31
+ else {
32
+ console.error('Static files directory not found at:', staticPath);
33
+ }
34
+ // PM2 API endpoints
35
+ app.get('/api/processes', (req, res) => {
36
+ pm2_1.default.connect((err) => {
37
+ if (err) {
38
+ console.error(err);
39
+ res.status(500).json({ error: 'Failed to connect to PM2' });
40
+ return;
41
+ }
42
+ pm2_1.default.list((err, processList) => {
43
+ pm2_1.default.disconnect();
44
+ if (err) {
45
+ console.error(err);
46
+ res.status(500).json({ error: 'Failed to get PM2 processes' });
47
+ return;
48
+ }
49
+ res.json(processList);
50
+ });
51
+ });
52
+ });
53
+ // Action endpoints (start, stop, restart, delete)
54
+ app.post('/api/process/:id/:action', (req, res) => {
55
+ const { id, action } = req.params;
56
+ pm2_1.default.connect((err) => {
57
+ if (err) {
58
+ console.error(err);
59
+ res.status(500).json({ error: 'Failed to connect to PM2' });
60
+ return;
61
+ }
62
+ const processAction = (actionName, cb) => {
63
+ switch (actionName) {
64
+ case 'start':
65
+ pm2_1.default.start(id, cb);
66
+ break;
67
+ case 'stop':
68
+ pm2_1.default.stop(id, cb);
69
+ break;
70
+ case 'restart':
71
+ pm2_1.default.restart(id, cb);
72
+ break;
73
+ case 'delete':
74
+ pm2_1.default.delete(id, cb);
75
+ break;
76
+ default:
77
+ cb(new Error('Invalid action'));
78
+ }
79
+ };
80
+ processAction(action, (err) => {
81
+ pm2_1.default.disconnect();
82
+ if (err) {
83
+ console.error(err);
84
+ res.status(500).json({ error: `Failed to ${action} process` });
85
+ return;
86
+ }
87
+ res.json({ success: true, message: `Process ${id} ${action} request received` });
88
+ });
89
+ });
90
+ });
91
+ // Get system metrics
92
+ app.get('/api/metrics', (req, res) => {
93
+ const metrics = {
94
+ loadAvg: os_1.default.loadavg(),
95
+ memory: {
96
+ total: os_1.default.totalmem(),
97
+ free: os_1.default.freemem(),
98
+ used: os_1.default.totalmem() - os_1.default.freemem()
99
+ },
100
+ uptime: os_1.default.uptime(),
101
+ cpus: os_1.default.cpus().length
102
+ };
103
+ res.json(metrics);
104
+ });
105
+ // Get process logs
106
+ app.get('/api/logs/:id/:type', (req, res) => {
107
+ const { id, type } = req.params;
108
+ const logType = type === 'err' ? 'err' : 'out';
109
+ pm2_1.default.connect((err) => {
110
+ if (err) {
111
+ console.error(err);
112
+ res.status(500).json({ error: 'Failed to connect to PM2' });
113
+ return;
114
+ }
115
+ pm2_1.default.describe(id, (err, processDesc) => {
116
+ var _a, _b;
117
+ if (err || !processDesc || processDesc.length === 0) {
118
+ pm2_1.default.disconnect();
119
+ res.status(404).json({ error: 'Process not found' });
120
+ return;
121
+ }
122
+ const logPath = (_b = (_a = processDesc[0]) === null || _a === void 0 ? void 0 : _a.pm2_env) === null || _b === void 0 ? void 0 : _b[`pm_${logType}_log_path`];
123
+ if (!logPath) {
124
+ pm2_1.default.disconnect();
125
+ res.status(404).json({ error: `Log file for ${logType} not found` });
126
+ return;
127
+ }
128
+ try {
129
+ // Read the log file using Node.js fs instead of exec
130
+ const fs = require('fs');
131
+ let logContent = '';
132
+ if (fs.existsSync(logPath)) {
133
+ // Read the last part of the file (up to 10KB to avoid huge responses)
134
+ const stats = fs.statSync(logPath);
135
+ const fileSize = stats.size;
136
+ const readSize = Math.min(fileSize, 10 * 1024); // 10KB max
137
+ const position = Math.max(0, fileSize - readSize);
138
+ const buffer = Buffer.alloc(readSize);
139
+ const fd = fs.openSync(logPath, 'r');
140
+ fs.readSync(fd, buffer, 0, readSize, position);
141
+ fs.closeSync(fd);
142
+ logContent = buffer.toString('utf8');
143
+ }
144
+ const logs = logContent.split('\n').filter((line) => line.trim() !== '');
145
+ pm2_1.default.disconnect();
146
+ res.json({ logs });
147
+ }
148
+ catch (error) {
149
+ console.error(`Error reading log file: ${error}`);
150
+ pm2_1.default.disconnect();
151
+ res.status(500).json({ error: 'Failed to read log file' });
152
+ }
153
+ });
154
+ });
155
+ });
156
+ // WebSocket for real-time updates
157
+ io.on('connection', (socket) => {
158
+ console.log('Client connected');
159
+ // Send process updates every 3 seconds
160
+ const processInterval = setInterval(() => {
161
+ pm2_1.default.connect((err) => {
162
+ if (err) {
163
+ console.error(err);
164
+ return;
165
+ }
166
+ pm2_1.default.list((err, processList) => {
167
+ pm2_1.default.disconnect();
168
+ if (err) {
169
+ console.error(err);
170
+ return;
171
+ }
172
+ socket.emit('processes', processList);
173
+ });
174
+ });
175
+ }, 3000);
176
+ // Send system metrics every 2 seconds
177
+ const metricsInterval = setInterval(() => {
178
+ const metrics = {
179
+ loadAvg: os_1.default.loadavg(),
180
+ memory: {
181
+ total: os_1.default.totalmem(),
182
+ free: os_1.default.freemem(),
183
+ used: os_1.default.totalmem() - os_1.default.freemem()
184
+ },
185
+ uptime: os_1.default.uptime(),
186
+ cpus: os_1.default.cpus().length
187
+ };
188
+ socket.emit('metrics', metrics);
189
+ }, 2000);
190
+ socket.on('disconnect', () => {
191
+ console.log('Client disconnected');
192
+ clearInterval(processInterval);
193
+ clearInterval(metricsInterval);
194
+ });
195
+ }); // Catch-all route to return the React app
196
+ app.get('*', (req, res) => {
197
+ const indexPath = 'D:/Personal/ezpm2gui/src/client/build/index.html';
198
+ console.log('Trying to serve index.html from:', indexPath);
199
+ const fs = require('fs');
200
+ if (fs.existsSync(indexPath)) {
201
+ res.sendFile(indexPath);
202
+ }
203
+ else {
204
+ console.error('Index.html file not found at:', indexPath);
205
+ res.status(404).send('File not found. Please check server configuration.');
206
+ }
207
+ });
208
+ // Return the server instance
209
+ return server;
210
+ }
211
+ // Only start the server if this file is run directly
212
+ if (require.main === module) {
213
+ const PORT = process.env.PORT || 3001;
214
+ const HOST = process.env.HOST || 'localhost';
215
+ const server = createServer();
216
+ server.listen(PORT, () => {
217
+ console.log(`Server running on http://${HOST}:${PORT}`);
218
+ });
219
+ }
package/install.bat ADDED
@@ -0,0 +1,22 @@
1
+ @echo off
2
+ echo Installing client dependencies...
3
+ cd src\client
4
+ call npm install
5
+
6
+ echo Building client...
7
+ call npm run build
8
+
9
+ echo Installing server dependencies...
10
+ cd ..\..
11
+ call npm install
12
+
13
+ echo Building server...
14
+ call npm run build
15
+
16
+ echo.
17
+ echo ======================================
18
+ echo Installation complete!
19
+ echo.
20
+ echo To start the application, run:
21
+ echo npm start
22
+ echo ======================================
package/install.sh ADDED
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ echo "Installing client dependencies..."
4
+ cd src/client
5
+ npm install
6
+
7
+ echo "Building client..."
8
+ npm run build
9
+
10
+ echo "Installing server dependencies..."
11
+ cd ../..
12
+ npm install
13
+
14
+ echo "Building server..."
15
+ npm run build
16
+
17
+ echo ""
18
+ echo "======================================"
19
+ echo "Installation complete!"
20
+ echo ""
21
+ echo "To start the application, run:"
22
+ echo "npm start"
23
+ echo "======================================="
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "ezpm2gui",
3
+ "version": "1.0.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 \"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 start",
14
+ "build": "npm run build:server && npm run build:client",
15
+ "build:server": "tsc",
16
+ "build:client": "cd src/client && npm run build",
17
+ "prepare": "npm run build",
18
+ "test": "echo \"Error: no test specified\" && exit 1",
19
+ "postinstall": "node scripts/postinstall.js"
20
+ },
21
+ "keywords": ["pm2", "gui", "monitor", "process-manager", "dashboard"],
22
+ "author": "Chandan Bhagat",
23
+ "license": "ISC",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/thechandanbhagat/ezpm2gui.git"
27
+ },
28
+ "description": "A modern web-based GUI for PM2 process manager",
29
+ "dependencies": {
30
+ "axios": "^1.9.0",
31
+ "chart.js": "^4.4.9",
32
+ "express": "^4.18.3",
33
+ "pm2": "^6.0.5",
34
+ "react": "^19.1.0",
35
+ "react-dom": "^19.1.0",
36
+ "react-scripts": "^5.0.1",
37
+ "socket.io": "^4.8.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/express": "^4.17.21",
41
+ "@types/node": "^22.15.17",
42
+ "@types/react": "^19.1.3",
43
+ "@types/react-dom": "^19.1.4",
44
+ "@types/socket.io": "^3.0.2",
45
+ "concurrently": "^9.1.2",
46
+ "nodemon": "^3.1.10",
47
+ "typescript": "^5.8.3"
48
+ }
49
+ }
@@ -0,0 +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.');
@@ -0,0 +1,13 @@
1
+ {
2
+ "files": {
3
+ "main.css": "/static/css/main.c1cbda3a.css",
4
+ "main.js": "/static/js/main.dde30e92.js",
5
+ "index.html": "/index.html",
6
+ "main.c1cbda3a.css.map": "/static/css/main.c1cbda3a.css.map",
7
+ "main.dde30e92.js.map": "/static/js/main.dde30e92.js.map"
8
+ },
9
+ "entrypoints": [
10
+ "static/css/main.c1cbda3a.css",
11
+ "static/js/main.dde30e92.js"
12
+ ]
13
+ }
@@ -0,0 +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="ezPM2GUI - A modern interface for PM2 process manager"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>ezPM2GUI - PM2 Process Manager</title><script defer="defer" src="/static/js/main.dde30e92.js"></script><link href="/static/css/main.c1cbda3a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "short_name": "ezPM2GUI",
3
+ "name": "ezPM2GUI - Modern PM2 Interface",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ },
10
+ {
11
+ "src": "logo192.png",
12
+ "type": "image/png",
13
+ "sizes": "192x192"
14
+ },
15
+ {
16
+ "src": "logo512.png",
17
+ "type": "image/png",
18
+ "sizes": "512x512"
19
+ }
20
+ ],
21
+ "start_url": ".",
22
+ "display": "standalone",
23
+ "theme_color": "#4a90e2",
24
+ "background_color": "#f8f9fa"
25
+ }