ezpm2gui 1.0.0 → 1.2.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.
Files changed (58) hide show
  1. package/README.md +202 -14
  2. package/bin/ezpm2gui.js +40 -44
  3. package/bin/ezpm2gui.ts +51 -0
  4. package/bin/generate-ecosystem.js +24 -22
  5. package/bin/generate-ecosystem.ts +56 -0
  6. package/dist/server/config/project-configs.json +236 -0
  7. package/dist/server/index.js +64 -19
  8. package/dist/server/logs/deployment.log +12 -0
  9. package/dist/server/routes/clusterManagement.d.ts +3 -0
  10. package/dist/server/routes/clusterManagement.js +152 -0
  11. package/dist/server/routes/deployApplication.d.ts +3 -0
  12. package/dist/server/routes/deployApplication.js +310 -0
  13. package/dist/server/routes/logStreaming.d.ts +5 -0
  14. package/dist/server/routes/logStreaming.js +276 -0
  15. package/dist/server/routes/modules.d.ts +3 -0
  16. package/dist/server/routes/modules.js +106 -0
  17. package/dist/server/routes/processConfig.d.ts +3 -0
  18. package/dist/server/routes/processConfig.js +118 -0
  19. package/dist/server/routes/remoteConnections.d.ts +3 -0
  20. package/dist/server/routes/remoteConnections.js +634 -0
  21. package/dist/server/services/ProjectSetupService.d.ts +72 -0
  22. package/dist/server/services/ProjectSetupService.js +327 -0
  23. package/dist/server/utils/dialog.d.ts +1 -0
  24. package/dist/server/utils/dialog.js +16 -0
  25. package/dist/server/utils/encryption.d.ts +12 -0
  26. package/dist/server/utils/encryption.js +72 -0
  27. package/dist/server/utils/pm2-connection.d.ts +16 -0
  28. package/dist/server/utils/pm2-connection.js +141 -0
  29. package/dist/server/utils/remote-connection.d.ts +152 -0
  30. package/dist/server/utils/remote-connection.js +590 -0
  31. package/dist/server/utils/upload.d.ts +3 -0
  32. package/dist/server/utils/upload.js +39 -0
  33. package/package.json +65 -49
  34. package/src/client/build/asset-manifest.json +6 -6
  35. package/src/client/build/favicon.ico +2 -0
  36. package/src/client/build/index.html +1 -1
  37. package/src/client/build/logo192.svg +7 -0
  38. package/src/client/build/logo512.svg +7 -0
  39. package/src/client/build/manifest.json +5 -6
  40. package/src/client/build/static/css/main.672b8f26.css +2 -0
  41. package/src/client/build/static/css/main.672b8f26.css.map +1 -0
  42. package/src/client/build/static/js/main.31323a04.js +156 -0
  43. package/src/client/build/static/js/{main.dde30e92.js.LICENSE.txt → main.31323a04.js.LICENSE.txt} +19 -0
  44. package/src/client/build/static/js/main.31323a04.js.map +1 -0
  45. package/ .npmignore +0 -39
  46. package/install.bat +0 -22
  47. package/install.sh +0 -23
  48. package/src/client/build/static/css/main.c1cbda3a.css +0 -2
  49. package/src/client/build/static/css/main.c1cbda3a.css.map +0 -1
  50. package/src/client/build/static/js/main.dde30e92.js +0 -3
  51. package/src/client/build/static/js/main.dde30e92.js.map +0 -1
  52. package/src/client/package-lock.json +0 -16192
  53. package/src/client/package.json +0 -39
  54. package/src/client/public/index.html +0 -20
  55. package/src/client/public/manifest.json +0 -25
  56. package/src/index.ts +0 -24
  57. package/src/server/index.ts +0 -240
  58. package/tsconfig.json +0 -18
@@ -0,0 +1,236 @@
1
+ {
2
+ "projectTypes": {
3
+ "node": {
4
+ "name": "Node.js",
5
+ "detection": {
6
+ "files": ["package.json"],
7
+ "extensions": [".js", ".ts", ".jsx", ".tsx"]
8
+ },
9
+ "setup": {
10
+ "steps": [
11
+ {
12
+ "name": "Check Node.js version",
13
+ "command": "node --version",
14
+ "description": "Verifying Node.js installation",
15
+ "required": true
16
+ },
17
+ {
18
+ "name": "Install dependencies",
19
+ "command": "npm install",
20
+ "description": "Installing project dependencies",
21
+ "required": true,
22
+ "workingDirectory": "project"
23
+ },
24
+ {
25
+ "name": "Build project (if build script exists)",
26
+ "command": "npm run build",
27
+ "description": "Building the project",
28
+ "required": false,
29
+ "conditional": "build_script_exists",
30
+ "workingDirectory": "project"
31
+ },
32
+ {
33
+ "name": "Run tests (if test script exists)",
34
+ "command": "npm test",
35
+ "description": "Running tests",
36
+ "required": false,
37
+ "conditional": "test_script_exists",
38
+ "workingDirectory": "project"
39
+ }
40
+ ],
41
+ "environment": {
42
+ "NODE_ENV": "production"
43
+ }
44
+ },
45
+ "validation": {
46
+ "checks": [
47
+ {
48
+ "name": "package.json exists",
49
+ "file": "package.json"
50
+ },
51
+ {
52
+ "name": "node_modules exists after install",
53
+ "directory": "node_modules"
54
+ }
55
+ ]
56
+ },
57
+ "defaultConfig": {
58
+ "interpreter": "node",
59
+ "execMode": "fork",
60
+ "supportsCluster": true,
61
+ "startScript": "npm start"
62
+ }
63
+ },
64
+ "python": {
65
+ "name": "Python",
66
+ "detection": {
67
+ "files": ["requirements.txt", "pyproject.toml", "setup.py", "Pipfile"],
68
+ "extensions": [".py"]
69
+ },
70
+ "setup": {
71
+ "steps": [
72
+ {
73
+ "name": "Check Python version",
74
+ "command": "python --version",
75
+ "description": "Verifying Python installation",
76
+ "required": true
77
+ },
78
+ {
79
+ "name": "Create virtual environment",
80
+ "command": "python -m venv venv",
81
+ "description": "Creating Python virtual environment",
82
+ "required": true,
83
+ "workingDirectory": "project"
84
+ },
85
+ {
86
+ "name": "Activate virtual environment (Windows)",
87
+ "command": ".\\venv\\Scripts\\Activate.ps1",
88
+ "description": "Activating virtual environment",
89
+ "required": true,
90
+ "platform": "win32",
91
+ "workingDirectory": "project"
92
+ },
93
+ {
94
+ "name": "Activate virtual environment (Unix)",
95
+ "command": "source venv/bin/activate",
96
+ "description": "Activating virtual environment",
97
+ "required": true,
98
+ "platform": "unix",
99
+ "workingDirectory": "project"
100
+ },
101
+ {
102
+ "name": "Upgrade pip",
103
+ "command": "python -m pip install --upgrade pip",
104
+ "description": "Upgrading pip",
105
+ "required": true,
106
+ "workingDirectory": "project",
107
+ "useVenv": true
108
+ },
109
+ {
110
+ "name": "Install requirements",
111
+ "command": "pip install -r requirements.txt",
112
+ "description": "Installing Python dependencies",
113
+ "required": true,
114
+ "conditional": "requirements_exists",
115
+ "workingDirectory": "project",
116
+ "useVenv": true
117
+ },
118
+ {
119
+ "name": "Install from pyproject.toml",
120
+ "command": "pip install -e .",
121
+ "description": "Installing from pyproject.toml",
122
+ "required": true,
123
+ "conditional": "pyproject_exists",
124
+ "workingDirectory": "project",
125
+ "useVenv": true
126
+ }
127
+ ],
128
+ "environment": {
129
+ "PYTHONPATH": ".",
130
+ "PYTHON_UNBUFFERED": "1"
131
+ }
132
+ },
133
+ "validation": {
134
+ "checks": [
135
+ {
136
+ "name": "Virtual environment created",
137
+ "directory": "venv"
138
+ },
139
+ {
140
+ "name": "Requirements file exists",
141
+ "file": "requirements.txt",
142
+ "optional": true
143
+ }
144
+ ]
145
+ },
146
+ "defaultConfig": {
147
+ "interpreter": "python",
148
+ "execMode": "fork",
149
+ "supportsCluster": false,
150
+ "interpreterPath": "venv/Scripts/python.exe"
151
+ }
152
+ },
153
+ "dotnet": {
154
+ "name": ".NET",
155
+ "detection": {
156
+ "files": ["*.csproj", "*.fsproj", "*.vbproj", "*.sln"],
157
+ "extensions": [".cs", ".fs", ".vb"]
158
+ },
159
+ "setup": {
160
+ "steps": [
161
+ {
162
+ "name": "Check .NET version",
163
+ "command": "dotnet --version",
164
+ "description": "Verifying .NET installation",
165
+ "required": true
166
+ },
167
+ {
168
+ "name": "Restore packages",
169
+ "command": "dotnet restore",
170
+ "description": "Restoring NuGet packages",
171
+ "required": true,
172
+ "workingDirectory": "project"
173
+ },
174
+ {
175
+ "name": "Build project",
176
+ "command": "dotnet build --configuration Release",
177
+ "description": "Building .NET project",
178
+ "required": true,
179
+ "workingDirectory": "project"
180
+ },
181
+ {
182
+ "name": "Publish project",
183
+ "command": "dotnet publish --configuration Release --output ./publish",
184
+ "description": "Publishing .NET project",
185
+ "required": true,
186
+ "workingDirectory": "project"
187
+ },
188
+ {
189
+ "name": "Run tests",
190
+ "command": "dotnet test",
191
+ "description": "Running .NET tests",
192
+ "required": false,
193
+ "conditional": "test_project_exists",
194
+ "workingDirectory": "project"
195
+ }
196
+ ],
197
+ "environment": {
198
+ "DOTNET_ENVIRONMENT": "Production",
199
+ "ASPNETCORE_ENVIRONMENT": "Production"
200
+ }
201
+ },
202
+ "validation": {
203
+ "checks": [
204
+ {
205
+ "name": "Project file exists",
206
+ "pattern": "*.csproj"
207
+ },
208
+ {
209
+ "name": "Publish directory exists",
210
+ "directory": "publish"
211
+ }
212
+ ]
213
+ },
214
+ "defaultConfig": {
215
+ "interpreter": "dotnet",
216
+ "execMode": "fork",
217
+ "supportsCluster": false,
218
+ "startCommand": "dotnet run"
219
+ }
220
+ }
221
+ },
222
+ "global": {
223
+ "timeouts": {
224
+ "setup": 300000,
225
+ "validation": 30000
226
+ },
227
+ "retries": {
228
+ "setup": 2,
229
+ "validation": 1
230
+ },
231
+ "logging": {
232
+ "level": "info",
233
+ "file": "deployment.log"
234
+ }
235
+ }
236
+ }
@@ -9,6 +9,13 @@ const http_1 = __importDefault(require("http"));
9
9
  const socket_io_1 = require("socket.io");
10
10
  const pm2_1 = __importDefault(require("pm2"));
11
11
  const os_1 = __importDefault(require("os"));
12
+ const processConfig_1 = __importDefault(require("./routes/processConfig"));
13
+ const deployApplication_1 = __importDefault(require("./routes/deployApplication"));
14
+ const modules_1 = __importDefault(require("./routes/modules"));
15
+ const remoteConnections_1 = __importDefault(require("./routes/remoteConnections"));
16
+ const logStreaming_1 = require("./routes/logStreaming");
17
+ const pm2_connection_1 = require("./utils/pm2-connection");
18
+ const remote_connection_1 = require("./utils/remote-connection");
12
19
  /**
13
20
  * Create and configure the express server
14
21
  */
@@ -21,7 +28,10 @@ function createServer() {
21
28
  origin: '*',
22
29
  methods: ['GET', 'POST']
23
30
  }
24
- }); // Serve static files from the React app build directory
31
+ });
32
+ // Configure middleware
33
+ app.use(express_1.default.json());
34
+ // Serve static files from the React app build directory
25
35
  const staticPath = 'D:/Personal/ezpm2gui/src/client/build';
26
36
  console.log('Serving static files from:', staticPath);
27
37
  const fs = require('fs');
@@ -31,24 +41,33 @@ function createServer() {
31
41
  else {
32
42
  console.error('Static files directory not found at:', staticPath);
33
43
  }
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);
44
+ // Register routes app.use('/api/cluster', clusterManagementRoutes);
45
+ app.use('/api/config', processConfig_1.default);
46
+ app.use('/api/deploy', deployApplication_1.default);
47
+ app.use('/api/modules', modules_1.default);
48
+ app.use('/api/remote', remoteConnections_1.default);
49
+ // Setup log streaming with Socket.IO
50
+ (0, logStreaming_1.setupLogStreaming)(io); // PM2 API endpoints
51
+ app.get('/api/processes', async (req, res) => {
52
+ try {
53
+ const processList = await (0, pm2_connection_1.executePM2Command)((callback) => {
54
+ pm2_1.default.list(callback);
50
55
  });
51
- });
56
+ res.json(processList);
57
+ }
58
+ catch (err) {
59
+ console.error('Failed to get PM2 processes:', err);
60
+ // Check if error is about PM2 not being installed
61
+ if (err instanceof Error && err.message.includes('PM2 is not installed')) {
62
+ res.status(500).json({
63
+ error: 'PM2 is not installed. Please install PM2 globally using: npm install -g pm2',
64
+ pmNotInstalled: true
65
+ });
66
+ }
67
+ else {
68
+ res.status(500).json({ error: 'Failed to get PM2 processes' });
69
+ }
70
+ }
52
71
  });
53
72
  // Action endpoints (start, stop, restart, delete)
54
73
  app.post('/api/process/:id/:action', (req, res) => {
@@ -192,7 +211,8 @@ function createServer() {
192
211
  clearInterval(processInterval);
193
212
  clearInterval(metricsInterval);
194
213
  });
195
- }); // Catch-all route to return the React app
214
+ });
215
+ // Catch-all route to return the React app
196
216
  app.get('*', (req, res) => {
197
217
  const indexPath = 'D:/Personal/ezpm2gui/src/client/build/index.html';
198
218
  console.log('Trying to serve index.html from:', indexPath);
@@ -216,4 +236,29 @@ if (require.main === module) {
216
236
  server.listen(PORT, () => {
217
237
  console.log(`Server running on http://${HOST}:${PORT}`);
218
238
  });
239
+ // Handle shutdown gracefully
240
+ process.on('SIGINT', async () => {
241
+ console.log('\nGracefully shutting down...');
242
+ try {
243
+ await (0, pm2_connection_1.disconnectFromPM2)();
244
+ await remote_connection_1.remoteConnectionManager.closeAllConnections();
245
+ }
246
+ catch (error) {
247
+ console.error('Error during shutdown:', error);
248
+ }
249
+ server.close();
250
+ process.exit(0);
251
+ });
252
+ process.on('SIGTERM', async () => {
253
+ console.log('\nGracefully shutting down...');
254
+ try {
255
+ await (0, pm2_connection_1.disconnectFromPM2)();
256
+ await remote_connection_1.remoteConnectionManager.closeAllConnections();
257
+ }
258
+ catch (error) {
259
+ console.error('Error during shutdown:', error);
260
+ }
261
+ server.close();
262
+ process.exit(0);
263
+ });
219
264
  }
@@ -0,0 +1,12 @@
1
+ [2025-05-25T18:02:41.395Z] Starting setup for python project at: D:\Projects\FaceDetection
2
+ [2025-05-25T18:02:41.396Z] Executing step: Check Python version
3
+ [2025-05-25T18:02:41.954Z] Step "Check Python version" completed with exit code: 0
4
+ [2025-05-25T18:02:41.955Z] Executing step: Create virtual environment
5
+ [2025-05-25T18:03:20.998Z] Step "Create virtual environment" completed with exit code: 0
6
+ [2025-05-25T18:03:20.999Z] Executing step: Activate virtual environment (Windows)
7
+ [2025-05-25T18:03:21.176Z] Step "Activate virtual environment (Windows)" completed with exit code: 0
8
+ [2025-05-25T18:03:21.176Z] Executing step: Upgrade pip
9
+ [2025-05-25T18:03:48.644Z] Step "Upgrade pip" completed with exit code: 0
10
+ [2025-05-25T18:03:48.645Z] Executing step: Install requirements
11
+ [2025-05-25T18:04:56.318Z] Step "Install requirements" completed with exit code: 0
12
+ [2025-05-25T18:04:56.322Z] Setup completed. Success: true
@@ -0,0 +1,3 @@
1
+ import { Router } from 'express';
2
+ declare const router: Router;
3
+ export default router;
@@ -0,0 +1,152 @@
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
+ const express_1 = require("express");
7
+ const pm2_1 = __importDefault(require("pm2"));
8
+ const router = (0, express_1.Router)();
9
+ // Get cluster information for a specific process
10
+ router.get('/:id', (req, res) => {
11
+ const { id } = req.params;
12
+ pm2_1.default.connect((err) => {
13
+ if (err) {
14
+ console.error(err);
15
+ res.status(500).json({ error: 'Failed to connect to PM2' });
16
+ return;
17
+ }
18
+ pm2_1.default.describe(id, (err, processDesc) => {
19
+ pm2_1.default.disconnect();
20
+ if (err || !processDesc || processDesc.length === 0) {
21
+ res.status(404).json({ error: 'Process not found' });
22
+ return;
23
+ }
24
+ const process = processDesc[0];
25
+ const clusterInfo = {
26
+ pm_id: process.pm_id,
27
+ name: process.name,
28
+ instances: process.pm2_env.instances,
29
+ exec_mode: process.pm2_env.exec_mode,
30
+ isCluster: process.pm2_env.exec_mode === 'cluster_mode'
31
+ };
32
+ res.json(clusterInfo);
33
+ });
34
+ });
35
+ });
36
+ // Scale process (change number of instances)
37
+ router.post('/:id/scale', (req, res) => {
38
+ const { id } = req.params;
39
+ const { instances } = req.body;
40
+ if (!instances || isNaN(parseInt(instances))) {
41
+ res.status(400).json({ error: 'Valid number of instances is required' });
42
+ return;
43
+ }
44
+ pm2_1.default.connect((err) => {
45
+ if (err) {
46
+ console.error(err);
47
+ res.status(500).json({ error: 'Failed to connect to PM2' });
48
+ return;
49
+ }
50
+ // Using PM2 API to scale a process
51
+ // TypeScript doesn't recognize 'scale' method, so we're using it with a type assertion
52
+ pm2_1.default.scale(id, parseInt(instances), (err) => {
53
+ pm2_1.default.disconnect();
54
+ if (err) {
55
+ console.error(err);
56
+ res.status(500).json({ error: 'Failed to scale process' });
57
+ return;
58
+ }
59
+ res.json({ success: true, message: `Process ${id} scaled to ${instances} instances` });
60
+ });
61
+ });
62
+ });
63
+ // Reload instances (zero-downtime reload)
64
+ router.post('/:id/reload', (req, res) => {
65
+ const { id } = req.params;
66
+ pm2_1.default.connect((err) => {
67
+ if (err) {
68
+ console.error(err);
69
+ res.status(500).json({ error: 'Failed to connect to PM2' });
70
+ return;
71
+ }
72
+ pm2_1.default.reload(id, (err) => {
73
+ pm2_1.default.disconnect();
74
+ if (err) {
75
+ console.error(err);
76
+ res.status(500).json({ error: 'Failed to reload process' });
77
+ return;
78
+ }
79
+ res.json({ success: true, message: `Process ${id} reloaded with zero-downtime` });
80
+ });
81
+ });
82
+ });
83
+ // Change execution mode between 'fork' and 'cluster'
84
+ router.post('/:id/exec-mode', (req, res) => {
85
+ const { id } = req.params;
86
+ const { mode } = req.body;
87
+ if (!mode || (mode !== 'fork' && mode !== 'cluster')) {
88
+ res.status(400).json({ error: 'Valid execution mode (fork or cluster) is required' });
89
+ return;
90
+ }
91
+ pm2_1.default.connect((err) => {
92
+ if (err) {
93
+ console.error(err);
94
+ res.status(500).json({ error: 'Failed to connect to PM2' });
95
+ return;
96
+ }
97
+ // First get the current process information to preserve settings
98
+ pm2_1.default.describe(id, (err, processDesc) => {
99
+ if (err || !processDesc || processDesc.length === 0) {
100
+ pm2_1.default.disconnect();
101
+ res.status(404).json({ error: 'Process not found' });
102
+ return;
103
+ }
104
+ const process = processDesc[0];
105
+ const pm2Env = process.pm2_env;
106
+ // Update the execution mode
107
+ const updateOptions = {
108
+ script: pm2Env.pm_exec_path,
109
+ name: process.name,
110
+ instances: pm2Env.instances || 1,
111
+ exec_mode: mode === 'cluster' ? 'cluster_mode' : 'fork_mode',
112
+ // Preserve other settings
113
+ cwd: pm2Env.pm_cwd,
114
+ watch: pm2Env.watch || false,
115
+ ignore_watch: pm2Env.ignore_watch || [],
116
+ env: pm2Env.env || {}
117
+ };
118
+ // Stop the existing process
119
+ pm2_1.default.stop(id, (stopErr) => {
120
+ if (stopErr) {
121
+ pm2_1.default.disconnect();
122
+ console.error(stopErr);
123
+ res.status(500).json({ error: 'Failed to stop process for exec mode change' });
124
+ return;
125
+ }
126
+ // Delete the existing process
127
+ pm2_1.default.del(id, (delErr) => {
128
+ if (delErr) {
129
+ pm2_1.default.disconnect();
130
+ console.error(delErr);
131
+ res.status(500).json({ error: 'Failed to delete process for exec mode change' });
132
+ return;
133
+ }
134
+ // Start with new settings
135
+ pm2_1.default.start(updateOptions, (startErr) => {
136
+ pm2_1.default.disconnect();
137
+ if (startErr) {
138
+ console.error(startErr);
139
+ res.status(500).json({ error: 'Failed to restart process with new exec mode' });
140
+ return;
141
+ }
142
+ res.json({
143
+ success: true,
144
+ message: `Process ${process.name} execution mode changed to ${mode}`
145
+ });
146
+ });
147
+ });
148
+ });
149
+ });
150
+ });
151
+ });
152
+ exports.default = router;
@@ -0,0 +1,3 @@
1
+ import { Router } from 'express';
2
+ declare const router: Router;
3
+ export default router;